/**
 * routes/host.js — RosterChirp-Host control plane
 *
 * All routes require the HOST_ADMIN_KEY header.
 * These routes operate on the 'public' schema (tenant registry).
 * They provision/deprovision per-tenant schemas.
 *
 * APP_TYPE must be 'host' for these routes to be registered.
 */

const express = require('express');
const router  = express.Router();
const {
  query, queryOne, queryResult, exec,
  runMigrations, ensureSchema,
  seedSettings, seedEventTypes, seedAdmin, seedUserGroups,
  refreshTenantCache,
} = require('../models/db');

const HOST_ADMIN_KEY = process.env.HOST_ADMIN_KEY || '';

// ── Host admin key guard ──────────────────────────────────────────────────────

function hostAdminMiddleware(req, res, next) {
  if (!HOST_ADMIN_KEY) {
    return res.status(503).json({ error: 'HOST_ADMIN_KEY is not configured' });
  }
  const key = req.headers['x-host-admin-key'] || req.headers['authorization']?.replace('Bearer ', '');
  if (!key || key !== HOST_ADMIN_KEY) {
    return res.status(401).json({ error: 'Invalid host admin key' });
  }
  next();
}

// All routes in this file require the host admin key
router.use(hostAdminMiddleware);

// ── Helpers ───────────────────────────────────────────────────────────────────

function slugToSchema(slug) {
  return `tenant_${slug.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
}

function isValidSlug(slug) {
  return /^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$/.test(slug);
}

async function reloadTenantCache() {
  const tenants = await query('public', "SELECT * FROM tenants WHERE status = 'active'");
  refreshTenantCache(tenants);
  return tenants;
}

// ── GET /api/host/tenants — list all tenants ──────────────────────────────────

router.get('/tenants', async (req, res) => {
  try {
    const tenants = await query('public',
      'SELECT * FROM tenants ORDER BY created_at DESC'
    );
    res.json({ tenants });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── GET /api/host/tenants/:slug — get single tenant ───────────────────────────

router.get('/tenants/:slug', async (req, res) => {
  try {
    const tenant = await queryOne('public',
      'SELECT * FROM tenants WHERE slug = $1', [req.params.slug]
    );
    if (!tenant) return res.status(404).json({ error: 'Tenant not found' });
    res.json({ tenant });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── POST /api/host/tenants — provision a new tenant ───────────────────────────
//
// Body: { slug, name, plan, adminEmail, adminName, adminPass, customDomain? }
//
// This:
//  1. Validates the slug (becomes subdomain + schema name)
//  2. Creates the Postgres schema
//  3. Runs all migrations in the new schema
//  4. Seeds settings, event types, and the first admin user
//  5. Records the tenant in the registry
//  6. Reloads the tenant domain cache

router.post('/tenants', async (req, res) => {
  const { slug, name, plan, adminEmail, adminName, adminPass, customDomain } = req.body;

  if (!slug || !name) return res.status(400).json({ error: 'slug and name are required' });
  if (!isValidSlug(slug)) {
    return res.status(400).json({
      error: 'slug must be 3-32 lowercase alphanumeric characters or hyphens, starting and ending with alphanumeric'
    });
  }

  const schemaName = slugToSchema(slug);

  try {
    // Check slug not already taken
    const existing = await queryOne('public',
      'SELECT id FROM tenants WHERE slug = $1', [slug]
    );
    if (existing) return res.status(400).json({ error: `Tenant '${slug}' already exists` });

    if (customDomain) {
      const domainTaken = await queryOne('public',
        'SELECT id FROM tenants WHERE custom_domain = $1', [customDomain.toLowerCase()]
      );
      if (domainTaken) return res.status(400).json({ error: `Custom domain '${customDomain}' is already in use` });
    }

    console.log(`[Host] Provisioning tenant: ${slug} (schema: ${schemaName})`);

    // 1. Create schema + run migrations
    await runMigrations(schemaName);

    // 2. Seed settings (uses env defaults unless overridden by body)
    await seedSettings(schemaName);

    // 3. Seed event types
    await seedEventTypes(schemaName);

    // 3b. Seed default user groups (Coaches, Players, Parents)
    await seedUserGroups(schemaName);

    // 4. Seed admin user — temporarily override env vars for this tenant
    const origEmail = process.env.ADMIN_EMAIL;
    const origName  = process.env.ADMIN_NAME;
    const origPass  = process.env.ADMIN_PASS;
    if (adminEmail) process.env.ADMIN_EMAIL = adminEmail;
    if (adminName)  process.env.ADMIN_NAME  = adminName;
    if (adminPass)  process.env.ADMIN_PASS  = adminPass;

    await seedAdmin(schemaName);

    process.env.ADMIN_EMAIL = origEmail;
    process.env.ADMIN_NAME  = origName;
    process.env.ADMIN_PASS  = origPass;

    // 5. Set app_type based on plan
    const planAppType = { chat: 'RosterChirp-Chat', brand: 'RosterChirp-Brand', team: 'RosterChirp-Team' }[plan] || 'RosterChirp-Chat';
    await exec(schemaName, "UPDATE settings SET value=$1 WHERE key='app_type'", [planAppType]);
    if (plan === 'brand' || plan === 'team') {
      await exec(schemaName, "UPDATE settings SET value='true' WHERE key='feature_branding'");
    }
    if (plan === 'team') {
      await exec(schemaName, "UPDATE settings SET value='true' WHERE key='feature_group_manager'");
      await exec(schemaName, "UPDATE settings SET value='true' WHERE key='feature_schedule_manager'");
    }

    // 6. Register in tenants table
    const tr = await queryResult('public', `
      INSERT INTO tenants (slug, name, schema_name, custom_domain, plan, admin_email)
      VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING *
    `, [slug, name, schemaName, customDomain?.toLowerCase() || null, plan || 'chat', adminEmail || null]);

    // 7. Reload domain cache
    await reloadTenantCache();

    const baseDomain = process.env.HOST_DOMAIN || 'rosterchirp.com';
    const tenant = tr.rows[0];
    tenant.url = `https://${slug}.${baseDomain}`;

    console.log(`[Host] Tenant provisioned: ${slug} → ${schemaName}`);
    res.status(201).json({ tenant });

  } catch (e) {
    console.error(`[Host] Provisioning failed for ${slug}:`, e.message);
    // Attempt cleanup of partially-created schema
    try {
      await exec('public', `DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`);
      console.log(`[Host] Cleaned up schema ${schemaName} after failed provision`);
    } catch (cleanupErr) {
      console.error(`[Host] Cleanup failed:`, cleanupErr.message);
    }
    res.status(500).json({ error: e.message });
  }
});

// ── PATCH /api/host/tenants/:slug — update tenant ─────────────────────────────
//
// Supports updating: name, plan, customDomain, status

router.patch('/tenants/:slug', async (req, res) => {
  const { name, plan, customDomain, status } = req.body;
  try {
    const tenant = await queryOne('public',
      'SELECT * FROM tenants WHERE slug = $1', [req.params.slug]
    );
    if (!tenant) return res.status(404).json({ error: 'Tenant not found' });

    if (customDomain && customDomain !== tenant.custom_domain) {
      const taken = await queryOne('public',
        'SELECT id FROM tenants WHERE custom_domain=$1 AND slug!=$2',
        [customDomain.toLowerCase(), req.params.slug]
      );
      if (taken) return res.status(400).json({ error: 'Custom domain already in use' });
    }

    if (status && !['active','suspended'].includes(status))
      return res.status(400).json({ error: 'status must be active or suspended' });

    await exec('public', `
      UPDATE tenants SET
        name          = COALESCE($1, name),
        plan          = COALESCE($2, plan),
        custom_domain = $3,
        status        = COALESCE($4, status),
        updated_at    = NOW()
      WHERE slug = $5
    `, [name || null, plan || null, customDomain?.toLowerCase() ?? tenant.custom_domain, status || null, req.params.slug]);

    // If plan changed, update feature flags in tenant schema
    if (plan && plan !== tenant.plan) {
      const s = tenant.schema_name;
      await exec(s, "UPDATE settings SET value=CASE WHEN $1 IN ('brand','team') THEN 'true' ELSE 'false' END WHERE key='feature_branding'", [plan]);
      await exec(s, "UPDATE settings SET value=CASE WHEN $1 = 'team' THEN 'true' ELSE 'false' END WHERE key='feature_group_manager'", [plan]);
      await exec(s, "UPDATE settings SET value=CASE WHEN $1 = 'team' THEN 'true' ELSE 'false' END WHERE key='feature_schedule_manager'", [plan]);
      const planAppType = { chat: 'RosterChirp-Chat', brand: 'RosterChirp-Brand', team: 'RosterChirp-Team' }[plan] || 'RosterChirp-Chat';
      await exec(s, "UPDATE settings SET value=$1 WHERE key='app_type'", [planAppType]);
    }

    await reloadTenantCache();
    const updated = await queryOne('public', 'SELECT * FROM tenants WHERE slug=$1', [req.params.slug]);
    res.json({ tenant: updated });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── DELETE /api/host/tenants/:slug — deprovision tenant ───────────────────────
//
// Permanently drops the tenant's Postgres schema and all data.
// Requires confirmation: body must include { confirm: "DELETE {slug}" }

router.delete('/tenants/:slug', async (req, res) => {
  const { confirm } = req.body;
  if (confirm !== `DELETE ${req.params.slug}`) {
    return res.status(400).json({
      error: `Confirmation required. Send { "confirm": "DELETE ${req.params.slug}" } in the request body.`
    });
  }

  try {
    const tenant = await queryOne('public',
      'SELECT * FROM tenants WHERE slug=$1', [req.params.slug]
    );
    if (!tenant) return res.status(404).json({ error: 'Tenant not found' });

    console.log(`[Host] Deprovisioning tenant: ${req.params.slug} (schema: ${tenant.schema_name})`);

    // Drop the entire schema — CASCADE removes all tables, indexes, triggers
    await exec('public', `DROP SCHEMA IF EXISTS "${tenant.schema_name}" CASCADE`);

    // Remove from registry
    await exec('public', 'DELETE FROM tenants WHERE slug=$1', [req.params.slug]);

    await reloadTenantCache();

    console.log(`[Host] Tenant deprovisioned: ${req.params.slug}`);
    res.json({ success: true, message: `Tenant '${req.params.slug}' and all its data have been permanently deleted.` });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── POST /api/host/tenants/:slug/migrate — run pending migrations ─────────────
//
// Useful after deploying a new migration file to apply it to all tenants.

router.post('/tenants/:slug/migrate', async (req, res) => {
  try {
    const tenant = await queryOne('public', 'SELECT * FROM tenants WHERE slug=$1', [req.params.slug]);
    if (!tenant) return res.status(404).json({ error: 'Tenant not found' });
    await runMigrations(tenant.schema_name);
    await seedSettings(tenant.schema_name);
    await seedEventTypes(tenant.schema_name);
    await seedUserGroups(tenant.schema_name);
    const applied = await query(tenant.schema_name, 'SELECT * FROM schema_migrations ORDER BY version');
    res.json({ success: true, migrations: applied });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── POST /api/host/migrate-all — run pending migrations on every tenant ───────

router.post('/migrate-all', async (req, res) => {
  try {
    const tenants = await query('public', "SELECT * FROM tenants WHERE status='active'");
    const results = [];
    for (const t of tenants) {
      try {
        await runMigrations(t.schema_name);
        // Also re-run seeding so new defaults (e.g. user groups, event types)
        // are applied to existing tenants that were provisioned before they existed.
        await seedSettings(t.schema_name);
        await seedEventTypes(t.schema_name);
        await seedUserGroups(t.schema_name);
        results.push({ slug: t.slug, status: 'ok' });
      } catch (e) {
        results.push({ slug: t.slug, status: 'error', error: e.message });
      }
    }
    res.json({ results });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// ── GET /api/host/status — host health check ──────────────────────────────────

router.get('/status', async (req, res) => {
  try {
    const tenantCount = await queryOne('public', 'SELECT COUNT(*) AS count FROM tenants');
    const active = await queryOne('public', "SELECT COUNT(*) AS count FROM tenants WHERE status='active'");
    const baseDomain = process.env.HOST_DOMAIN || 'rosterchirp.com';
    res.json({
      ok: true,
      appType: process.env.APP_TYPE || 'selfhost',
      baseDomain,
      tenants: { total: parseInt(tenantCount.count), active: parseInt(active.count) },
    });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

module.exports = router;
