/**
 * Company Routes
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { authenticate } from '../middleware/auth';
import { authenticateJwtOrApiToken } from '../middleware/dualAuth';
import { requirePermission } from '../middleware/permissions';
import { getModels } from '../models';
import { cloneCompany } from '../services/cloneService';
import { generateWithAI } from '../utils/aiProvider';

const router = express.Router();

/** Normalize a URL for duplicate comparison: lowercase, strip protocol & trailing slash */
function normalizeUrl(url: unknown): string {
  if (typeof url !== 'string') return '';
  let normalized = url.trim().toLowerCase();
  normalized = normalized.replace(/^https?:\/\//, ''); // remove protocol
  normalized = normalized.replace(/\/+$/, '');         // remove trailing slashes
  return normalized;
}

/** Normalize a company name for duplicate comparison: trim, collapse inner spaces, lowercase */
function normalizeName(name: unknown): string {
  if (typeof name !== 'string') return '';
  return name.trim().replace(/\s+/g, ' ').toLowerCase();
}

/** Normalize an email for duplicate comparison */
function normalizeEmail(email: unknown): string {
  if (typeof email !== 'string') return '';
  return email.trim().toLowerCase();
}

/**
 * Load the companies that may count as duplicates for this request.
 *
 * Duplicates are scoped to the companies the current user actually owns/belongs
 * to. A name, notification email or website URL used by some *other* account is
 * not a duplicate for this user, and previously produced a false
 * "Already Exists" error that made it impossible to create a company.
 */
async function getOwnCompanies(req: Request, excludeId?: string): Promise<any[]> {
  const { Company } = getModels();
  const userId = req.user!.id;
  const companyIds = (req.user!.companyIds || []).map(String).filter(Boolean);

  const scopes: any[] = [{ userIds: userId }];
  if (companyIds.length > 0) scopes.push({ _id: { $in: companyIds } });

  let companies: any[] = [];
  try {
    companies = (await Company.find({ $or: scopes })) || [];
  } catch {
    // A malformed id in companyIds would make the $in query throw — fall back to
    // ownership by userIds rather than blocking the user with a false duplicate.
    companies = (await Company.find({ userIds: userId })) || [];
  }

  // De-duplicate (a company can match both scopes) and drop the record being edited.
  const seen = new Set<string>();
  const result: any[] = [];
  for (const c of companies) {
    const id = String(c.id || c._id || '');
    if (seen.has(id)) continue;
    seen.add(id);
    if (excludeId && id === String(excludeId)) continue;
    result.push(c);
  }
  return result;
}

/**
 * Return the first field that is genuinely duplicated among the given companies,
 * comparing normalized values (exact match, not a partial/regex match).
 * Empty/blank values are never treated as duplicates.
 */
function findDuplicateField(
  companies: any[],
  fields: { name?: unknown; email?: unknown; websiteUrl?: unknown }
): 'name' | 'email' | 'url' | null {
  const name = normalizeName(fields.name);
  if (name && companies.some((c) => normalizeName(c?.name) === name)) return 'name';

  const email = normalizeEmail(fields.email);
  if (email && companies.some((c) => normalizeEmail(c?.notificationEmail) === email)) return 'email';

  const websiteUrl = normalizeUrl(fields.websiteUrl);
  if (websiteUrl && companies.some((c) => normalizeUrl(c?.websiteUrl) === websiteUrl)) return 'url';

  return null;
}

router.use(authenticateJwtOrApiToken);

// Get all companies for current user
router.get('/', async (req: Request, res: Response) => {
  try {
    const { Company } = getModels();
    const companies = await Company.find({
      _id: { $in: req.user!.companyIds },
    });

    res.json(companies);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get companies' });
  }
});

// Validate company fields (checks for duplicates in name/email/URL and gibberish names)
router.post(
  '/validate-name',
  [
    body('name').trim().notEmpty().withMessage('Name is required'),
    body('excludeId').optional().trim(),
    body('email').optional({ values: 'falsy' }).isEmail().withMessage('Please enter a valid Email ID'),
    body('websiteUrl').optional().trim(),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        // Check if the error is specifically about email format
        const emailError = errors.array().find((e: any) => e.param === 'email');
        if (emailError) {
          res.json({ valid: false, reason: 'invalid', field: 'email', message: emailError.msg });
          return;
        }
        // Other validation errors — fail-open so we don't block the user
        res.json({ valid: true });
        return;
      }

      const name = req.body.name.trim();
      const excludeId = req.body.excludeId;

      // --- Duplicate checks (name / email / URL) ---
      // Only the user's own companies count as duplicates, and only fields the
      // caller actually sent are checked, so an untouched/blank field can never
      // raise "Already Exists".
      const ownCompanies = await getOwnCompanies(req, excludeId);
      const duplicateField = findDuplicateField(ownCompanies, {
        name,
        email: req.body.email,
        websiteUrl: req.body.websiteUrl,
      });
      if (duplicateField) {
        res.json({ valid: false, reason: 'duplicate', field: duplicateField });
        return;
      }

      // --- AI gibberish detection for name (only if name is at least 2 chars) ---
      if (name.length >= 2) {
        const systemPrompt = `You are a company name validator. Determine if the given name appears to be a legitimate company name rather than random characters, keyboard mashing, or meaningless text. Consider: does it contain recognizable words, brand-like patterns, or plausible business naming conventions? Respond with JSON: { "valid": boolean, "reason": "brief explanation" }`;
        const prompt = `Is "${name}" a plausible company name?`;
        const userId = req.user?._id?.toString() || req.user?.id;

        const result = await generateWithAI(prompt, systemPrompt, 150, 0.1, 'json', undefined, undefined, userId);

        let parsed: { valid?: boolean; reason?: string };
        try {
          parsed = JSON.parse(result.content);
        } catch {
          // AI returned non-JSON — fail-open
          res.json({ valid: true });
          return;
        }

        if (typeof parsed.valid === 'boolean') {
          if (!parsed.valid) {
            res.json({ valid: false, reason: 'gibberish', field: 'name' });
            return;
          }
        }
        // If valid or unparseable, continue to next checks
      }

      // --- AI gibberish detection for email ---
      if (req.body.email && req.body.email.trim()) {
        const email = req.body.email.trim();
        const emailSystemPrompt = `You are an email address validator for business notifications. Determine if the given email address appears to be a legitimate business email rather than random characters, keyboard mashing, test data, or meaningless text. Consider: does the local part (before @) contain recognizable words, names, or plausible email patterns like firstname, firstname.lastname, or info/contact/sales? Is it a real email format rather than gibberish@domain.com? Respond with JSON: { "valid": boolean, "reason": "brief explanation" }`;
        const emailPrompt = `Is "${email}" a plausible notification email address for a real company?`;
        const userId = req.user?._id?.toString() || req.user?.id;

        try {
          const emailResult = await generateWithAI(emailPrompt, emailSystemPrompt, 150, 0.1, 'json', undefined, undefined, userId);
          let emailParsed: { valid?: boolean; reason?: string } | undefined;
          try {
            emailParsed = JSON.parse(emailResult.content);
          } catch {
            // AI returned non-JSON — fail-open, continue to next check
          }
          if (typeof emailParsed?.valid === 'boolean' && !emailParsed.valid) {
            res.json({ valid: false, reason: 'gibberish', field: 'email' });
            return;
          }
        } catch {
          // AI service failure for email — fail-open, continue to next check
        }
      }

      // --- AI gibberish detection for URL ---
      if (req.body.websiteUrl && req.body.websiteUrl.trim()) {
        const url = req.body.websiteUrl.trim();
        const urlSystemPrompt = `You are a website URL validator. Determine if the given URL appears to be a legitimate website address rather than random characters, keyboard mashing, test data, or meaningless text. Consider: does the domain contain recognizable words, brand names, or plausible website naming conventions? Is it a real website URL rather than something like https://asdfghjkl or https://test123? Respond with JSON: { "valid": boolean, "reason": "brief explanation" }`;
        const urlPrompt = `Is "${url}" a plausible website URL for a real company?`;
        const userId = req.user?._id?.toString() || req.user?.id;

        try {
          const urlResult = await generateWithAI(urlPrompt, urlSystemPrompt, 150, 0.1, 'json', undefined, undefined, userId);
          let urlParsed: { valid?: boolean; reason?: string } | undefined;
          try {
            urlParsed = JSON.parse(urlResult.content);
          } catch {
            // AI returned non-JSON — fail-open, continue
          }
          if (typeof urlParsed?.valid === 'boolean' && !urlParsed.valid) {
            res.json({ valid: false, reason: 'gibberish', field: 'url' });
            return;
          }
        } catch {
          // AI service failure for URL — fail-open, continue
        }
      }

      // All checks passed
      res.json({ valid: true });
    } catch (error: any) {
      // AI service failure — fail-open so we don't block legitimate users
      console.error('[Companies] Validate-name AI error:', error.message);
      res.json({ valid: true });
    }
  }
);

// Get single company
router.get('/:id', async (req: Request, res: Response) => {
  try {
    const { Company } = getModels();
    const { id } = req.params;

    if (!req.user!.companyIds.includes(id) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const company = await Company.findById(id);
    if (!company) {
      res.status(404).json({ error: 'Company not found' });
      return;
    }

    res.json(company);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get company' });
  }
});

// Create company
router.post(
  '/',
  requirePermission('business-profile', 'create'),
  [
    body('name').trim().notEmpty().withMessage('Company name is required'),
    body('notificationEmail').optional({ values: 'falsy' }).isEmail().withMessage('Please enter a valid Email ID'),
    body('websiteUrl').optional().trim(),
    body('description').optional().trim(),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const { User, Company } = getModels();

      // Duplicate checks — scoped to the user's own companies. A name, email or
      // URL already used by a different account is not a duplicate for this user.
      const ownCompanies = await getOwnCompanies(req);
      const duplicateField = findDuplicateField(ownCompanies, {
        name: req.body.name,
        email: req.body.notificationEmail,
        websiteUrl: req.body.websiteUrl,
      });
      if (duplicateField === 'name') {
        res.status(409).json({ error: 'Company name already exists', field: 'name' });
        return;
      }
      if (duplicateField === 'email') {
        res.status(409).json({ error: 'Notification email already exists', field: 'email' });
        return;
      }
      if (duplicateField === 'url') {
        res.status(409).json({ error: 'Website URL already exists', field: 'url' });
        return;
      }

      // Subscription and company limit check
      // Super-admin bypasses all subscription checks
      if (req.user!.role !== 'super-admin') {
        const { CompanySubscription, SubscriptionPackage } = getModels();

        // Find user's active subscription across all their companies
        let subscription: any = null;
        for (const cid of req.user!.companyIds) {
          subscription = await CompanySubscription.findOne({
            companyId: cid,
            status: { $in: ['active', 'trial'] },
          });
          if (subscription) break;
        }

        if (!subscription) {
          res.status(402).json({
            error: 'SUBSCRIPTION_REQUIRED',
            message: 'You need an active subscription to create a company. Please select a plan.',
          });
          return;
        }

        // Check if trial has expired
        if (subscription.status === 'trial' && subscription.trialEndDate) {
          if (new Date() > new Date(subscription.trialEndDate)) {
            res.status(402).json({
              error: 'TRIAL_EXPIRED',
              message: 'Your trial period has expired. Please upgrade to continue.',
            });
            return;
          }
        }

        // Check company count against plan limit
        const currentCompanyCount = req.user!.companyIds.length;
        const pkg = await SubscriptionPackage.findById(subscription.packageId);
        if (pkg) {
          const limits = pkg.limits?.toObject?.() || pkg.limits;
          const override = subscription.limitOverrides?.toObject?.() || subscription.limitOverrides;
          const companyLimit = override?.companies ?? limits.companies;
          if (companyLimit > 0 && currentCompanyCount >= companyLimit) {
            res.status(403).json({
              error: 'COMPANY_LIMIT_REACHED',
              message: `You have reached your company limit (${currentCompanyCount}/${companyLimit}). Please upgrade your subscription to create additional companies.`,
              limit: companyLimit,
              current: currentCompanyCount,
            });
            return;
          }
        }
      }

      const company = new Company({
        ...req.body,
        userIds: [req.user!.id],
      });

      await company.save();

      // Add company to user's company list
      req.user!.companyIds.push(company.id);
      await req.user!.save();

      res.status(201).json(company);
    } catch (error: any) {
      console.error('[Companies] Create error:', error);
      res.status(500).json({ error: 'Failed to create company', message: error.message });
    }
  }
);

// Update company
router.put('/:id', requirePermission('business-profile', 'edit'), [
  body('notificationEmail').optional({ values: 'falsy' }).isEmail().withMessage('Please enter a valid Email ID'),
], async (req: Request, res: Response) => {
  try {
    const validationErrors = validationResult(req);
    if (!validationErrors.isEmpty()) {
      res.status(400).json({ errors: validationErrors.array() });
      return;
    }

    const { Company } = getModels();
    const { id } = req.params;

    if (!req.user!.companyIds.includes(id) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const company = await Company.findById(id);
    if (!company) {
      res.status(404).json({ error: 'Company not found' });
      return;
    }

    // Duplicate checks — only for fields that are actually changing, compared
    // against the user's other companies (the record being edited is excluded,
    // so re-saving a company with its own name/email/URL is always allowed).
    const nameChanged =
      !!req.body.name && normalizeName(req.body.name) !== normalizeName(company.name || '');
    const emailChanged =
      !!req.body.notificationEmail &&
      !!req.body.notificationEmail.trim() &&
      normalizeEmail(req.body.notificationEmail) !== normalizeEmail(company.notificationEmail || '');
    const urlChanged =
      !!req.body.websiteUrl &&
      !!req.body.websiteUrl.trim() &&
      normalizeUrl(req.body.websiteUrl) !== normalizeUrl(company.websiteUrl || '');

    if (nameChanged || emailChanged || urlChanged) {
      const ownCompanies = await getOwnCompanies(req, id);
      const duplicateField = findDuplicateField(ownCompanies, {
        name: nameChanged ? req.body.name : undefined,
        email: emailChanged ? req.body.notificationEmail : undefined,
        websiteUrl: urlChanged ? req.body.websiteUrl : undefined,
      });
      if (duplicateField === 'name') {
        res.status(409).json({ error: 'Company name already exists', field: 'name' });
        return;
      }
      if (duplicateField === 'email') {
        res.status(409).json({ error: 'Notification email already exists', field: 'email' });
        return;
      }
      if (duplicateField === 'url') {
        res.status(409).json({ error: 'Website URL already exists', field: 'url' });
        return;
      }
    }

    // `updatedAt` is managed automatically by Mongoose (timestamps: true).
    // Assigning a string here previously risked a cast conflict — let the schema handle it.
    Object.assign(company, req.body);
    await company.save();

    res.json(company);
  } catch (error) {
    res.status(500).json({ error: 'Failed to update company' });
  }
});

// Toggle company active/inactive status
router.patch('/:id/status', requirePermission('business-profile', 'edit'), async (req: Request, res: Response) => {
  try {
    const { Company } = getModels();
    const { id } = req.params;
    const { isActive } = req.body;

    if (typeof isActive !== 'boolean') {
      res.status(400).json({ error: 'isActive must be a boolean' });
      return;
    }

    if (!req.user!.companyIds.includes(id)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Prevent deactivating the company the user is currently in
    if (!isActive && req.user!.activeCompanyId === id) {
      res.status(400).json({ error: 'Cannot deactivate the company you are currently using. Switch to another company first.' });
      return;
    }

    const company = await Company.findById(id);
    if (!company) {
      res.status(404).json({ error: 'Company not found' });
      return;
    }

    company.isActive = isActive;
    await company.save();

    res.json(company);
  } catch (error) {
    res.status(500).json({ error: 'Failed to update company status' });
  }
});

// Delete company
router.delete('/:id', requirePermission('business-profile', 'delete'), async (req: Request, res: Response) => {
  try {
    const { User, Company } = getModels();
    const { id } = req.params;

    if (!req.user!.companyIds.includes(id) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    await Company.findByIdAndDelete(id);

    // Remove from user's company list
    req.user!.companyIds = req.user!.companyIds.filter((cid) => cid !== id);
    await req.user!.save();

    res.json({ message: 'Company deleted successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete company' });
  }
});

// Clone company with all data
router.post(
  '/:id/clone',
  requirePermission('business-profile', 'create'),
  [
    body('name').optional().trim(),
    body('includeContent').optional().isBoolean(),
    body('includeAIContext').optional().isBoolean(),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const { id } = req.params;
      const { name, includeContent = true, includeAIContext = false } = req.body;

      // Verify access to source company
      if (!req.user!.companyIds.includes(id) && req.user!.role !== 'admin') {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      // Get source company name for default clone name
      const { Company } = getModels();
      const sourceCompany = await Company.findById(id);
      if (!sourceCompany) {
        res.status(404).json({ error: 'Source company not found' });
        return;
      }

      const cloneName = name?.trim() || `${sourceCompany.name} (Copy)`;

      // Check for duplicate clone name among the user's own companies only
      const ownCompanies = await getOwnCompanies(req);
      if (findDuplicateField(ownCompanies, { name: cloneName }) === 'name') {
        res.status(409).json({ error: 'Company name already exists', field: 'name' });
        return;
      }

      const result = await cloneCompany(id, cloneName, req.user!.id, {
        includeContent,
        includeAIContext,
      });

      res.status(201).json({
        company: result.company,
        stats: result.stats,
      });
    } catch (error: any) {
      console.error('[Companies] Clone error:', error);
      res.status(500).json({
        error: 'Failed to clone company',
        message: error.message,
      });
    }
  }
);

// Get company stats (dashboard summary)
router.get('/:id/stats', async (req: Request, res: Response) => {
  try {
    const { ModuleData, Company } = getModels();
    const { id } = req.params;

    if (!req.user!.companyIds.includes(id) && req.user!.role !== 'admin' && req.user!.role !== 'super-admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const company = await Company.findById(id);
    if (!company) {
      res.status(404).json({ error: 'Company not found' });
      return;
    }

    // Count module data entries for this company
    const moduleDataCount = await ModuleData.countDocuments({ companyId: id });

    res.json({
      companyId: id,
      companyName: company.name,
      totalModules: moduleDataCount,
      isActive: company.isActive,
      createdAt: company.createdAt,
      updatedAt: company.updatedAt,
    });
  } catch (error) {
    console.error('[Companies] Stats error:', error);
    res.status(500).json({ error: 'Failed to get company stats' });
  }
});

export default router;
