/**
 * Email Signature Routes
 *
 * CRUD + listing (search / filter / sort / pagination) + duplicate for
 * enterprise email signatures. Mirrors the Wikipedia Profile route
 * architecture: same middleware stack, same permission-key convention, same
 * response envelope. AI generation, exports, test-email and bulk operations are
 * introduced in later phases.
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticateJwtOrApiToken } from '../middleware/dualAuth';
import { requireCompanyAccess } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';

const router = express.Router();

// Auth middleware (JWT or API token) — same as Wikipedia Profile
router.use(authenticateJwtOrApiToken);

// ============================================
// CONSTANTS
// ============================================

/**
 * Structured content sections. On update, each provided section is shallow
 * merged into the stored one so partial saves never wipe untouched fields —
 * the same intent as Wikipedia Profile's `profileData` merge, applied per
 * section now that the document is properly structured.
 */
const OBJECT_SECTIONS = [
  'basicInformation',
  'contactInformation',
  'branding',
  'about',
  'qrCode',
  'calendarBooking',
  'legal',
  'customContent',
  'designSettings',
  'linkedData',
] as const;

/** Array sections are replaced wholesale when provided. */
const ARRAY_SECTIONS = ['socialLinks', 'callToActions', 'certifications', 'dataSources'] as const;

/** Scalar top-level fields a client may set directly. */
const SCALAR_FIELDS = [
  'signatureName', 'profileType', 'businessProfileId', 'founderId', 'employeeId',
  'template', 'status', 'isDefault', 'language', 'previewHtml', 'exportHtml', 'version',
] as const;

// ============================================
// TENANT OWNERSHIP GUARD
// ============================================

/**
 * Load a signature by id and enforce tenant ownership before any record-specific
 * operation. `requirePermission` only checks that the caller holds the permission
 * in *their own* organisation — it never verifies the target record belongs to
 * that organisation. Without this guard a non-admin with the permission could
 * read/modify/duplicate/delete another org's record by its id.
 *
 * Semantics:
 *  - Super Admin / Admin keep the existing bypass (mirrors `requireCompanyAccess`).
 *  - Non-admins may only touch records whose `companyId` is one of their own.
 *  - A record owned by another org returns **404** (not 403) so the endpoint
 *    cannot be used to enumerate which ids exist.
 *
 * Returns the loaded document, or `null` after having sent the response (caller
 * must stop). Does not alter the API response format.
 */
async function loadOwnedSignature(req: Request, res: Response): Promise<any | null> {
  const { EmailSignature } = getModels();
  const doc = await EmailSignature.findById(req.params.id);

  const notFound = () => {
    res.status(404).json({ error: 'Email signature not found' });
    return null;
  };

  if (!doc) return notFound();

  const user = (req as any).user;
  const isAdmin = user?.role === 'super-admin' || user?.role === 'admin';
  if (!isAdmin) {
    const ownedCompanies: string[] = user?.companyIds || [];
    if (!ownedCompanies.includes(String(doc.companyId))) {
      // Belongs to another org — respond identically to a missing record.
      return notFound();
    }
  }

  return doc;
}

// ============================================
// VALIDATION
// ============================================

const createValidation = [
  body('companyId').isString().notEmpty().withMessage('Company ID is required'),
  body('profileType').isIn(['company', 'founder', 'employee']).withMessage('Invalid profile type'),
  body('signatureName').isString().notEmpty().withMessage('Signature name is required').trim().isLength({ max: 300 }),
  body('businessProfileId').optional().isString(),
  body('founderId').optional().isString(),
  body('employeeId').optional().isString(),
  body('template').optional().isString(),
  body('status').optional().isIn(['draft', 'active', 'archived']).withMessage('Invalid status'),
  body('isDefault').optional().isBoolean(),
  body('language').optional().isString(),
  body('basicInformation').optional().isObject(),
  body('contactInformation').optional().isObject(),
  body('branding').optional().isObject(),
  body('about').optional().isObject(),
  body('socialLinks').optional().isArray(),
  body('callToActions').optional().isArray(),
  body('qrCode').optional().isObject(),
  body('calendarBooking').optional().isObject(),
  body('legal').optional().isObject(),
  body('certifications').optional().isArray(),
  body('customContent').optional().isObject(),
  body('designSettings').optional().isObject(),
];

const updateValidation = [
  body('signatureName').optional().isString().notEmpty().trim().isLength({ max: 300 }),
  body('profileType').optional().isIn(['company', 'founder', 'employee']),
  body('status').optional().isIn(['draft', 'active', 'archived']),
  body('isDefault').optional().isBoolean(),
  body('template').optional().isString(),
  body('language').optional().isString(),
  body('basicInformation').optional().isObject(),
  body('contactInformation').optional().isObject(),
  body('branding').optional().isObject(),
  body('about').optional().isObject(),
  body('socialLinks').optional().isArray(),
  body('callToActions').optional().isArray(),
  body('qrCode').optional().isObject(),
  body('calendarBooking').optional().isObject(),
  body('legal').optional().isObject(),
  body('certifications').optional().isArray(),
  body('customContent').optional().isObject(),
  body('designSettings').optional().isObject(),
];

// ============================================
// ROUTES
// ============================================

/**
 * GET /detail/:id — Get a single email signature by ID
 *
 * MUST be defined before the /:companyId catch-all so Express
 * doesn't match "detail" as a companyId prefix.
 */
router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const signature = await loadOwnedSignature(req, res);
    if (!signature) return; // response already sent by the guard
    const obj = signature.toObject();
    res.json({
      data: {
        ...obj,
        id: obj._id?.toString?.() || obj._id,
      },
    });
  } catch (error: any) {
    console.error('[EmailSignature] Get error:', error);
    res.status(500).json({ error: 'Failed to fetch email signature' });
  }
});

/**
 * GET /:companyId — List all email signatures for a company
 */
router.get('/:companyId', requireCompanyAccess, async (req: Request, res: Response) => {
  try {
    const { EmailSignature } = getModels();
    const { companyId } = req.params;
    const {
      page = 1,
      limit = 20,
      search,
      profileType,
      status,
      template,
      isDefault,
      language,
      sortBy = 'createdAt',
      sortOrder = 'desc',
    } = req.query;

    const filter: any = { companyId };
    if (profileType) filter.profileType = profileType;
    if (status) filter.status = status;
    if (template) filter.template = template;
    if (language) filter.language = language;
    if (isDefault !== undefined) filter.isDefault = isDefault === 'true';

    if (search) {
      filter.$or = [
        { signatureName: { $regex: search, $options: 'i' } },
        { 'basicInformation.fullName': { $regex: search, $options: 'i' } },
        { 'basicInformation.companyName': { $regex: search, $options: 'i' } },
      ];
    }

    const pageNum = Math.max(1, Number(page));
    const limitNum = Math.min(100, Math.max(1, Number(limit)));
    const skip = (pageNum - 1) * limitNum;

    const sortDir = sortOrder === 'asc' ? 1 : -1;
    const sort: any = { [sortBy as string]: sortDir };

    const [signatures, total] = await Promise.all([
      EmailSignature.find(filter).sort(sort).skip(skip).limit(limitNum).lean(),
      EmailSignature.countDocuments(filter),
    ]);

    const result = signatures.map((s: any) => ({
      ...s,
      id: s._id?.toString?.() || s._id,
    }));

    res.json({
      data: result,
      total,
      page: pageNum,
      totalPages: Math.ceil(total / limitNum),
    });
  } catch (error: any) {
    console.error('[EmailSignature] List error:', error);
    res.status(500).json({ error: 'Failed to fetch email signatures' });
  }
});

/**
 * POST / — Create a new email signature
 */
router.post('/', requirePermission('email-signature', 'create'), createValidation, async (req: Request, res: Response) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  try {
    const { EmailSignature } = getModels();
    const user = (req as any).user;
    const userId = user?.userId || user?.id || 'unknown';

    const signature = await EmailSignature.create({
      ...req.body,
      createdBy: userId,
      updatedBy: userId,
    });

    res.status(201).json({
      data: {
        ...signature.toObject(),
        id: signature._id?.toString?.() || signature._id,
      },
    });
  } catch (error: any) {
    console.error('[EmailSignature] Create error:', error);
    res.status(500).json({ error: 'Failed to create email signature' });
  }
});

/**
 * PUT /:id — Update an email signature (per-section shallow merge)
 */
router.put('/:id', requirePermission('email-signature', 'edit'), updateValidation, async (req: Request, res: Response) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  try {
    const { EmailSignature } = getModels();
    const { id } = req.params;
    const user = (req as any).user;

    // Tenant ownership guard (404 if missing or owned by another org)
    const existingDoc = await loadOwnedSignature(req, res);
    if (!existingDoc) return;
    const existing = existingDoc.toObject();

    const update: any = {};

    // Scalar fields — set directly when provided
    for (const field of SCALAR_FIELDS) {
      if (req.body[field] !== undefined) update[field] = req.body[field];
    }

    // Object sections — shallow merge with the stored section
    for (const section of OBJECT_SECTIONS) {
      if (req.body[section] !== undefined) {
        const current = (existing as any)[section] || {};
        const currentObj = current?.toObject?.() || current;
        update[section] = { ...currentObj, ...req.body[section] };
      }
    }

    // Array sections — replace wholesale when provided
    for (const section of ARRAY_SECTIONS) {
      if (req.body[section] !== undefined) update[section] = req.body[section];
    }

    update.updatedBy = user?.userId || user?.id || 'unknown';

    const updated = await EmailSignature.findByIdAndUpdate(
      id,
      { $set: update },
      { new: true, runValidators: true }
    ).lean();

    if (!updated) {
      return res.status(404).json({ error: 'Email signature not found' });
    }

    res.json({
      data: {
        ...updated,
        id: updated._id?.toString?.() || updated._id,
      },
    });
  } catch (error: any) {
    console.error('[EmailSignature] Update error:', error);
    res.status(500).json({ error: 'Failed to update email signature' });
  }
});

/**
 * DELETE /:id — Delete a single email signature
 */
router.delete('/:id', requirePermission('email-signature', 'delete'), async (req: Request, res: Response) => {
  try {
    const { EmailSignature } = getModels();
    // Tenant ownership guard (404 if missing or owned by another org)
    const owned = await loadOwnedSignature(req, res);
    if (!owned) return;
    await EmailSignature.findByIdAndDelete(req.params.id);
    res.json({ message: 'Email signature deleted successfully' });
  } catch (error: any) {
    console.error('[EmailSignature] Delete error:', error);
    res.status(500).json({ error: 'Failed to delete email signature' });
  }
});

/**
 * POST /:id/duplicate — Duplicate an email signature
 *
 * Generates a new document ID, appends "Copy" to the name, resets timestamps
 * and AI/default flags, and preserves company scoping.
 */
router.post('/:id/duplicate', requirePermission('email-signature', 'create'), async (req: Request, res: Response) => {
  try {
    const { EmailSignature } = getModels();
    const user = (req as any).user;
    const userId = user?.userId || user?.id || 'unknown';

    // Tenant ownership guard (404 if missing or owned by another org)
    const originalDoc = await loadOwnedSignature(req, res);
    if (!originalDoc) return;
    const original = originalDoc.toObject();

    // Strip identity/audit fields so a fresh document is created cleanly.
    const {
      _id, id: _origId, createdAt, updatedAt, __v,
      ...rest
    } = original as any;

    const duplicate = await EmailSignature.create({
      ...rest,
      signatureName: (original.signatureName || 'Untitled') + ' (Copy)',
      isDefault: false,          // a copy is never the default
      status: 'draft',           // copies start as drafts
      version: 1,
      createdBy: userId,
      updatedBy: userId,
      lastGeneratedAt: undefined,
    });

    res.status(201).json({
      data: {
        ...duplicate.toObject(),
        id: duplicate._id?.toString?.() || duplicate._id,
      },
    });
  } catch (error: any) {
    console.error('[EmailSignature] Duplicate error:', error);
    res.status(500).json({ error: 'Failed to duplicate email signature' });
  }
});

export default router;
