/**
 * Website Deployments API — /api/website-deployments
 *
 * Enqueue and track deployments of a generated Website Planner site to a hosting
 * connection. The websiteDeployWorker drains the queue asynchronously (bundle → upload →
 * live). Direct clone of the landing-page deployments API, adapted for the Website
 * Planner's ModuleData-backed storage (data.websitePlanners[]).
 *
 * Fully additive — new collection + namespace. The existing website generator, export,
 * and preview endpoints are untouched.
 */

import express, { Request, Response } from 'express';
import { authenticate } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';
import { getModels } from '../models';
import { hasGeneratedWebsite, NO_GENERATED_SITE_ERROR } from '../services/hosting/websiteBundler';
import { kickWebsiteDeployWorker } from '../services/hosting/websiteDeployWorker';

const router = express.Router();
router.use(authenticate);

const authorizeCompany = (req: Request, companyId: string): boolean =>
  req.user!.companyIds.includes(companyId) || req.user!.role === 'admin' || req.user!.role === 'super-admin';

/** Mirror a deployment status onto the website so the detail view stops showing a stale one. */
async function setWebsiteDeploymentStatus(companyId: string, websiteId: string, patch: Record<string, any>): Promise<void> {
  const { ModuleData } = getModels();
  const set: Record<string, any> = {};
  for (const [k, v] of Object.entries(patch)) set[`data.websitePlanners.$.deployment.${k}`] = v;
  await ModuleData.updateOne(
    { moduleId: 'websitePlanners', companyId, 'data.websitePlanners.id': websiteId },
    { $set: set },
  ).catch(() => undefined);
}

// Enqueue a deployment.
router.post('/', requirePermission('website-planner', 'edit'), async (req: Request, res: Response) => {
  try {
    const { companyId, websiteId, connectionId, customDomain } = req.body;
    if (!companyId || !authorizeCompany(req, companyId)) { res.status(403).json({ error: 'Access denied' }); return; }
    if (!websiteId || !connectionId) { res.status(400).json({ error: 'websiteId and connectionId are required' }); return; }

    const { HostingConnection, ModuleData, WebsiteDeployment } = getModels();
    const userId = req.user!._id.toString();

    const conn = await HostingConnection.findById(connectionId);
    if (!conn || conn.userId !== userId || conn.companyId !== companyId) { res.status(404).json({ error: 'Hosting connection not found' }); return; }

    const container = await ModuleData.findOne({ moduleId: 'websitePlanners', companyId });
    const website = (container?.data?.websitePlanners || []).find((w: any) => w.id === websiteId || w._id?.toString() === websiteId);
    if (!website) { res.status(404).json({ error: 'Website not found' }); return; }

    // Preflight the artifact here rather than letting the worker discover it a minute
    // later: the caller gets the real reason immediately and no failed record is left
    // behind to keep showing the error after the site has been generated.
    if (!hasGeneratedWebsite(websiteId, website)) {
      res.status(400).json({ error: NO_GENERATED_SITE_ERROR });
      return;
    }

    const deployment = new WebsiteDeployment({
      companyId,
      createdBy: userId,
      websiteId,
      websiteName: website.name || '',
      provider: conn.provider,
      connectionRef: conn._id.toString(),
      status: 'queued',
      attemptCount: 0,
      customDomain: customDomain || undefined,
    });
    await deployment.save();

    // Reflect "queued" on the website so the detail view updates immediately.
    await ModuleData.updateOne(
      { moduleId: 'websitePlanners', companyId, 'data.websitePlanners.id': websiteId },
      { $set: { 'data.websitePlanners.$.deployment.status': 'queued', 'data.websitePlanners.$.deployment.provider': conn.provider, 'data.websitePlanners.$.deployment.deploymentId': deployment._id.toString() } },
    ).catch(() => undefined);

    kickWebsiteDeployWorker();
    res.status(201).json(deployment);
  } catch (error: any) {
    res.status(500).json({ error: error?.message || 'Failed to enqueue deployment' });
  }
});

// List deployments (company-scoped, optional ?websiteId=).
router.get('/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) { res.status(403).json({ error: 'Access denied' }); return; }
    const { WebsiteDeployment } = getModels();
    const query: any = { companyId };
    if (req.query.websiteId) query.websiteId = String(req.query.websiteId);
    const list = await WebsiteDeployment.find(query).sort({ createdAt: -1 }).limit(100);
    res.json(list);
  } catch (error: any) {
    res.status(500).json({ error: error?.message || 'Failed to list deployments' });
  }
});

// Single deployment.
router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const { WebsiteDeployment } = getModels();
    const d = await WebsiteDeployment.findById(req.params.id);
    if (!d || !authorizeCompany(req, d.companyId)) { res.status(404).json({ error: 'Deployment not found' }); return; }
    res.json(d);
  } catch (error: any) {
    res.status(500).json({ error: error?.message || 'Failed to get deployment' });
  }
});

// Cancel a not-yet-live deployment.
router.post('/:id/cancel', requirePermission('website-planner', 'edit'), async (req: Request, res: Response) => {
  try {
    const { WebsiteDeployment } = getModels();
    const d = await WebsiteDeployment.findById(req.params.id);
    if (!d || d.createdBy !== req.user!._id.toString()) { res.status(404).json({ error: 'Deployment not found' }); return; }
    if (['live', 'failed', 'cancelled'].includes(d.status)) { res.status(400).json({ error: `Cannot cancel a ${d.status} deployment` }); return; }
    d.status = 'cancelled';
    d.workerLockedAt = null;
    d.nextAttemptAt = null;
    await d.save();
    // Don't leave the website advertising an in-progress publish that no longer exists.
    await setWebsiteDeploymentStatus(d.companyId, d.websiteId, { status: 'cancelled' });
    res.json(d);
  } catch (error: any) {
    res.status(500).json({ error: error?.message || 'Failed to cancel deployment' });
  }
});

// Re-deploy (enqueue a fresh deployment for the same website).
router.post('/:id/redeploy', requirePermission('website-planner', 'edit'), async (req: Request, res: Response) => {
  try {
    const { WebsiteDeployment, HostingConnection, ModuleData } = getModels();
    const userId = req.user!._id.toString();
    const prev = await WebsiteDeployment.findById(req.params.id);
    if (!prev || prev.createdBy !== userId) { res.status(404).json({ error: 'Deployment not found' }); return; }

    // Resolve a usable connection. Deleting the connection a past deploy used must not
    // strand the site: prefer an explicitly chosen one, then the original, then the
    // admin's most recent connection for this company.
    const owned = (c: any) => c && c.userId === userId && c.companyId === prev.companyId;
    let conn: any = null;
    if (req.body?.connectionId) {
      const requested = await HostingConnection.findById(String(req.body.connectionId));
      if (owned(requested)) conn = requested;
    }
    if (!conn) {
      const original = await HostingConnection.findById(prev.connectionRef);
      if (owned(original)) conn = original;
    }
    if (!conn) {
      conn = await HostingConnection.findOne({ companyId: prev.companyId, userId }).sort({ createdAt: -1 });
    }
    if (!conn) {
      res.status(400).json({ error: 'The hosting connection used for this deployment no longer exists. Add a hosting connection, then publish again.' });
      return;
    }

    const container = await ModuleData.findOne({ moduleId: 'websitePlanners', companyId: prev.companyId });
    const website = (container?.data?.websitePlanners || []).find((w: any) => w.id === prev.websiteId || w._id?.toString() === prev.websiteId);
    if (!hasGeneratedWebsite(prev.websiteId, website)) {
      res.status(400).json({ error: NO_GENERATED_SITE_ERROR });
      return;
    }

    const deployment = new WebsiteDeployment({
      companyId: prev.companyId,
      createdBy: prev.createdBy,
      websiteId: prev.websiteId,
      websiteName: prev.websiteName,
      provider: conn.provider,
      connectionRef: conn._id.toString(),
      status: 'queued',
      attemptCount: 0,
      customDomain: prev.customDomain,
    });
    await deployment.save();

    await setWebsiteDeploymentStatus(prev.companyId, prev.websiteId, {
      status: 'queued',
      provider: conn.provider,
      deploymentId: deployment._id.toString(),
    });

    kickWebsiteDeployWorker();
    res.status(201).json(deployment);
  } catch (error: any) {
    res.status(500).json({ error: error?.message || 'Failed to redeploy' });
  }
});

export default router;
