const express = require('express');
const db = require('../db/models');
const wrapAsync = require('../helpers').wrapAsync;
const { resolveTenantContextByHost } = require('../tenantContext');
const { setRequestContextScope } = require('../requestContext');

const router = express.Router();
const { Op } = db.Sequelize;

const SERVICE_CATALOG = {
  ai_ml: {
    lineNameFr: 'IA & Machine Learning',
    lineNameEn: 'AI & Machine Learning',
    lineDescriptionFr:
      'Automatisation, analytics et modèles sur mesure pour les entreprises africaines.',
    lineDescriptionEn:
      'Custom AI, analytics, and automation for African enterprises.',
    pricingModel: 'project_fixed',
    typicalTimeline: '16 semaines',
    serviceNameFr: 'Implémentation IA',
    serviceNameEn: 'AI Implementation',
    serviceDescriptionFr:
      'Discovery, préparation des données, modélisation, intégration et transfert.',
    serviceDescriptionEn:
      'Discovery, data preparation, modeling, integration and handover.',
    templateName: 'AI Implementation',
    templateDescription:
      'Discovery → Data → Model → Integration → Training → Handover',
    templateWeeks: 16,
    billingModel: 'fixed_price',
  },
  cybersecurity: {
    lineNameFr: 'Cybersécurité',
    lineNameEn: 'Cybersecurity',
    lineDescriptionFr:
      'Audits, remédiation et supervision pour sécuriser les SI critiques.',
    lineDescriptionEn:
      'Audits, remediation, and monitoring for critical information systems.',
    pricingModel: 'project_fixed',
    typicalTimeline: '4 semaines',
    serviceNameFr: 'Audit cybersécurité',
    serviceNameEn: 'Cybersecurity Audit',
    serviceDescriptionFr:
      'Scoping, assessment, remediation planning et reporting exécutif.',
    serviceDescriptionEn:
      'Scoping, assessment, remediation planning, and executive reporting.',
    templateName: 'Cybersecurity Audit',
    templateDescription: 'Scoping → Assessment → Remediation → Report',
    templateWeeks: 4,
    billingModel: 'fixed_price',
  },
  cloud: {
    lineNameFr: 'Cloud Solutions',
    lineNameEn: 'Cloud Solutions',
    lineDescriptionFr:
      'Architecture, migration et opérations cloud pour des équipes agiles.',
    lineDescriptionEn:
      'Architecture, migration, and cloud operations for agile teams.',
    pricingModel: 'hybrid',
    typicalTimeline: '8 semaines',
    serviceNameFr: 'Migration cloud',
    serviceNameEn: 'Cloud Migration',
    serviceDescriptionFr:
      'Audit, architecture cible, migration et validation des workloads.',
    serviceDescriptionEn:
      'Audit, target architecture, workload migration, and validation.',
    templateName: 'Cloud Migration',
    templateDescription: 'Audit → Architecture → Migration → Validation',
    templateWeeks: 8,
    billingModel: 'time_material',
  },
  hardware: {
    lineNameFr: 'Infrastructure matérielle',
    lineNameEn: 'Hardware Infrastructure',
    lineDescriptionFr:
      'Réseaux, équipements et déploiements terrain avec expertise locale.',
    lineDescriptionEn:
      'Networks, equipment, and field deployments with local expertise.',
    pricingModel: 'project_fixed',
    typicalTimeline: 'Variable',
    serviceNameFr: 'Déploiement hardware',
    serviceNameEn: 'Hardware Deployment',
    serviceDescriptionFr:
      'Approvisionnement, installation, tests et mise en production.',
    serviceDescriptionEn:
      'Procurement, installation, testing, and go-live.',
    templateName: 'Hardware Deployment',
    templateDescription: 'Procurement → Install → Test → Handover',
    templateWeeks: 6,
    billingModel: 'fixed_price',
  },
  web_mobile: {
    lineNameFr: 'Web & Mobile',
    lineNameEn: 'Web & Mobile',
    lineDescriptionFr:
      'Produits digitaux sur mesure, du cadrage jusqu’au déploiement.',
    lineDescriptionEn:
      'Custom digital products from discovery through deployment.',
    pricingModel: 'hybrid',
    typicalTimeline: '12 semaines',
    serviceNameFr: 'Développement web / mobile',
    serviceNameEn: 'Web / Mobile Development',
    serviceDescriptionFr:
      'Sprints agiles, UX, développement et stabilisation continue.',
    serviceDescriptionEn:
      'Agile sprints, UX, development, and continuous hardening.',
    templateName: 'Web/Mobile Development',
    templateDescription: 'Sprint setup → Build → QA → Launch',
    templateWeeks: 12,
    billingModel: 'time_material',
  },
  database: {
    lineNameFr: 'Database Management',
    lineNameEn: 'Database Management',
    lineDescriptionFr:
      'Architecture, migration et optimisation de données à grande échelle.',
    lineDescriptionEn:
      'Architecture, migration, and optimization for scalable data systems.',
    pricingModel: 'project_fixed',
    typicalTimeline: '6 semaines',
    serviceNameFr: 'Setup base de données',
    serviceNameEn: 'Database Setup',
    serviceDescriptionFr:
      'Modélisation, setup, migration et tuning des performances.',
    serviceDescriptionEn:
      'Modeling, setup, migration, and performance tuning.',
    templateName: 'Database Setup',
    templateDescription: 'Modeling → Setup → Migration → Optimization',
    templateWeeks: 6,
    billingModel: 'fixed_price',
  },
};

const COMPANY_SIZE_LABELS = {
  '1_10': '1–10 employés',
  '11_50': '11–50 employés',
  '51_200': '51–200 employés',
  '201_500': '201–500 employés',
  '501_1000': '501–1000 employés',
  '1000_plus': '1000+ employés',
};

const BUDGET_RANGE_LABELS = {
  lt_5k: '< 5 000 USD',
  '5_20k': '5 000–20 000 USD',
  '20_50k': '20 000–50 000 USD',
  '50k_plus': '50 000+ USD',
};

const BUDGET_ESTIMATES = {
  lt_5k: 3000,
  '5_20k': 12500,
  '20_50k': 35000,
  '50k_plus': 60000,
};

const CHANNEL_LABELS = {
  email: 'Email',
  phone: 'Téléphone',
  whatsapp: 'WhatsApp',
};

const URGENCY_LABELS = {
  standard: 'Standard',
  urgent: 'Urgent',
  critical: 'Critique',
};

const SALES_STAGE_BLUEPRINT = [
  {
    name: 'Nouveau besoin',
    probability_percent: 10,
    sort_order: 1,
    is_closed_won: false,
    is_closed_lost: false,
  },
  {
    name: 'Qualification',
    probability_percent: 30,
    sort_order: 2,
    is_closed_won: false,
    is_closed_lost: false,
  },
  {
    name: 'Proposition envoyée',
    probability_percent: 60,
    sort_order: 3,
    is_closed_won: false,
    is_closed_lost: false,
  },
  {
    name: 'Négociation',
    probability_percent: 80,
    sort_order: 4,
    is_closed_won: false,
    is_closed_lost: false,
  },
];

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function normalizeText(value) {
  if (typeof value !== 'string') {
    return '';
  }

  return value.trim().replace(/\s+/g, ' ');
}

function throwBadRequest(message) {
  const error = new Error(message);
  error.code = 400;
  throw error;
}

function splitFullName(fullName) {
  const parts = fullName.split(' ').filter(Boolean);
  return {
    firstName: parts[0] || fullName,
    lastName: parts.slice(1).join(' ') || '-',
  };
}

function getBudgetEstimate(range) {
  return BUDGET_ESTIMATES[range] || null;
}

function getScore(payload) {
  let score = 35;

  if (['11_50', '51_200', '201_500', '501_1000', '1000_plus'].includes(payload.company_size)) {
    score += 10;
  }

  if (['51_200', '201_500', '501_1000', '1000_plus'].includes(payload.company_size)) {
    score += 5;
  }

  if (payload.budget_range === '5_20k') {
    score += 10;
  }

  if (payload.budget_range === '20_50k') {
    score += 15;
  }

  if (payload.budget_range === '50k_plus') {
    score += 20;
  }

  if (['ai_ml', 'cybersecurity', 'cloud'].includes(payload.service_line)) {
    score += 10;
  }

  if (payload.urgency === 'urgent') {
    score += 5;
  }

  if (payload.urgency === 'critical') {
    score += 10;
  }

  if (payload.preferred_channel === 'whatsapp') {
    score += 5;
  }

  if (payload.message.length > 120) {
    score += 5;
  }

  return Math.min(score, 100);
}

function getScope(tenant, organization) {
  const scope = {};

  if (tenant?.id) {
    scope.tenantId = tenant.id;
  }

  if (organization?.id) {
    scope.organizationsId = organization.id;
  }

  return scope;
}

function addDays(days) {
  const date = new Date();
  date.setDate(date.getDate() + days);
  return date;
}

function buildLeadMessage(payload, catalog) {
  return [
    `Service demandé: ${catalog.lineNameFr}`,
    `Taille du client: ${COMPANY_SIZE_LABELS[payload.company_size]}`,
    `Budget estimé: ${BUDGET_RANGE_LABELS[payload.budget_range]}`,
    `Canal préféré: ${CHANNEL_LABELS[payload.preferred_channel]}`,
    `Urgence: ${URGENCY_LABELS[payload.urgency]}`,
    '',
    payload.message,
  ].join('\n');
}

function buildCompanyNote(payload) {
  return [
    'Fiche enrichie automatiquement depuis la landing page publique TSC FleetOS.',
    `Taille: ${COMPANY_SIZE_LABELS[payload.company_size]}`,
    `Dernier canal préféré: ${CHANNEL_LABELS[payload.preferred_channel]}`,
  ].join('\n');
}

function buildContactNote(payload) {
  return [
    'Lead entrant web.',
    `Canal préféré: ${CHANNEL_LABELS[payload.preferred_channel]}`,
    `Urgence: ${URGENCY_LABELS[payload.urgency]}`,
  ].join('\n');
}

function buildDealNote(payload, score, lead, catalog) {
  return [
    `Référence lead: TSC-${lead.id.split('-')[0].toUpperCase()}`,
    `Score: ${score}/100`,
    `Service recommandé: ${catalog.serviceNameFr}`,
    `Template suggéré: ${catalog.templateName}`,
    `Budget visé: ${BUDGET_RANGE_LABELS[payload.budget_range]}`,
    `Canal préféré: ${CHANNEL_LABELS[payload.preferred_channel]}`,
    `Urgence: ${URGENCY_LABELS[payload.urgency]}`,
    '',
    payload.message,
  ].join('\n');
}

async function attachScope(instance, tenant, organization, transaction) {
  if (tenant && typeof instance.setTenant === 'function') {
    await instance.setTenant(tenant, { transaction });
  }

  if (organization && typeof instance.setOrganizations === 'function') {
    await instance.setOrganizations(organization, { transaction });
  }
}

async function resolveTenantContext(req, transaction) {
  return resolveTenantContextByHost(req, {
    transaction,
    required: true,
  });
}

async function ensureSalesPipeline(tenant, organization, transaction) {
  const scope = getScope(tenant, organization);

  let pipeline = await db.pipelines.findOne({
    where: {
      pipeline_type: 'sales',
      ...scope,
    },
    order: [
      ['sort_order', 'ASC'],
      ['createdAt', 'ASC'],
    ],
    transaction,
  });

  if (!pipeline) {
    pipeline = await db.pipelines.create(
      {
        name: 'Pipeline commercial TSC',
        pipeline_type: 'sales',
        active: true,
        sort_order: 1,
      },
      { transaction },
    );

    await attachScope(pipeline, tenant, organization, transaction);
  }

  let firstStage = null;

  for (const stageBlueprint of SALES_STAGE_BLUEPRINT) {
    let stage = await db.deal_stages.findOne({
      where: {
        pipelineId: pipeline.id,
        name: stageBlueprint.name,
        ...scope,
      },
      transaction,
    });

    if (!stage) {
      stage = await db.deal_stages.create(stageBlueprint, { transaction });
      await attachScope(stage, tenant, organization, transaction);
      await stage.setPipeline(pipeline, { transaction });
    }

    if (!firstStage || stage.sort_order < firstStage.sort_order) {
      firstStage = stage;
    }
  }

  return {
    pipeline,
    firstStage,
  };
}

async function ensureCatalog(serviceLineKey, tenant, organization, transaction) {
  const catalog = SERVICE_CATALOG[serviceLineKey];

  if (!catalog) {
    throwBadRequest('Service line invalide.');
  }

  const scope = getScope(tenant, organization);

  let serviceLine = await db.service_lines.findOne({
    where: {
      category: serviceLineKey,
      ...scope,
    },
    transaction,
  });

  if (!serviceLine) {
    serviceLine = await db.service_lines.create(
      {
        name_fr: catalog.lineNameFr,
        name_en: catalog.lineNameEn,
        description_fr: catalog.lineDescriptionFr,
        description_en: catalog.lineDescriptionEn,
        category: serviceLineKey,
        pricing_model: catalog.pricingModel,
        typical_timeline: catalog.typicalTimeline,
        active: true,
      },
      { transaction },
    );

    await attachScope(serviceLine, tenant, organization, transaction);
  }

  let service = await db.services.findOne({
    where: {
      service_lineId: serviceLine.id,
      name_fr: catalog.serviceNameFr,
      ...scope,
    },
    transaction,
  });

  if (!service) {
    service = await db.services.create(
      {
        name_fr: catalog.serviceNameFr,
        name_en: catalog.serviceNameEn,
        description_fr: catalog.serviceDescriptionFr,
        description_en: catalog.serviceDescriptionEn,
        pricing_model: catalog.pricingModel,
        min_price: getBudgetEstimate('lt_5k'),
        max_price: getBudgetEstimate('50k_plus'),
        typical_duration_weeks: catalog.templateWeeks,
        active: true,
      },
      { transaction },
    );

    await attachScope(service, tenant, organization, transaction);
    await service.setService_line(serviceLine, { transaction });
  }

  let projectTemplate = await db.project_templates.findOne({
    where: {
      service_lineId: serviceLine.id,
      name: catalog.templateName,
      ...scope,
    },
    transaction,
  });

  if (!projectTemplate) {
    projectTemplate = await db.project_templates.create(
      {
        name: catalog.templateName,
        description: catalog.templateDescription,
        default_duration_weeks: catalog.templateWeeks,
        default_budget: getBudgetEstimate('20_50k'),
        billing_model: catalog.billingModel,
        active: true,
      },
      { transaction },
    );

    await attachScope(projectTemplate, tenant, organization, transaction);
    await projectTemplate.setService_line(serviceLine, { transaction });
  }

  return {
    serviceLine,
    service,
    projectTemplate,
    ...catalog,
  };
}

function validatePayload(payload) {
  if (!payload.full_name) {
    throwBadRequest('Le nom du contact est obligatoire.');
  }

  if (!payload.company_name) {
    throwBadRequest('Le nom de la société est obligatoire.');
  }

  if (!payload.email || !emailPattern.test(payload.email)) {
    throwBadRequest('Veuillez fournir un email professionnel valide.');
  }

  if (!SERVICE_CATALOG[payload.service_line]) {
    throwBadRequest('Veuillez sélectionner une ligne de service valide.');
  }

  if (!COMPANY_SIZE_LABELS[payload.company_size]) {
    throwBadRequest('Veuillez sélectionner la taille de votre entreprise.');
  }

  if (!BUDGET_RANGE_LABELS[payload.budget_range]) {
    throwBadRequest('Veuillez sélectionner un budget estimatif.');
  }

  if (!CHANNEL_LABELS[payload.preferred_channel]) {
    throwBadRequest('Veuillez sélectionner un canal de communication valide.');
  }

  if (!URGENCY_LABELS[payload.urgency]) {
    throwBadRequest('Veuillez sélectionner un niveau d’urgence valide.');
  }

  if (payload.preferred_channel !== 'email' && !payload.phone) {
    throwBadRequest('Le numéro de téléphone est requis pour un suivi par téléphone ou WhatsApp.');
  }

  if (!payload.message || payload.message.length < 24) {
    throwBadRequest('Décrivez votre besoin en au moins 24 caractères.');
  }
}

router.post(
  '/',
  wrapAsync(async (req, res) => {
    const payload = {
      full_name: normalizeText(req.body.full_name),
      company_name: normalizeText(req.body.company_name),
      email: normalizeText(req.body.email).toLowerCase(),
      phone: normalizeText(req.body.phone),
      service_line: normalizeText(req.body.service_line),
      company_size: req.body.company_size,
      budget_range: req.body.budget_range,
      preferred_channel: normalizeText(req.body.preferred_channel).toLowerCase(),
      urgency: normalizeText(req.body.urgency).toLowerCase(),
      message: normalizeText(req.body.message),
    };

    validatePayload(payload);

    const transaction = await db.sequelize.transaction();

    try {
      const { tenant, organization } = await resolveTenantContext(req, transaction);

      setRequestContextScope({
        currentOrg: organization.id,
        enforceOrganizationScope: true,
      });

      await db.sequelize.query(
        "select set_config('app.current_org', :currentOrg, false), set_config('app.global_access', 'false', false)",
        {
          replacements: { currentOrg: organization.id },
          transaction,
        },
      );

      const scope = getScope(tenant, organization);
      const catalog = await ensureCatalog(payload.service_line, tenant, organization, transaction);
      const { pipeline, firstStage } = await ensureSalesPipeline(tenant, organization, transaction);
      const { firstName, lastName } = splitFullName(payload.full_name);
      const score = getScore(payload);
      const leadStatus = score >= 70 ? 'qualified' : 'new';

      let company = await db.companies.findOne({
        where: {
          ...scope,
          [Op.or]: [
            { legal_name: { [Op.iLike]: payload.company_name } },
            { trade_name: { [Op.iLike]: payload.company_name } },
          ],
        },
        transaction,
      });

      if (!company) {
        company = await db.companies.create(
          {
            legal_name: payload.company_name,
            trade_name: payload.company_name,
            size_band: payload.company_size,
            country: 'DRC',
            lifecycle_stage: 'lead',
            notes: buildCompanyNote(payload),
          },
          { transaction },
        );

        await attachScope(company, tenant, organization, transaction);
      } else {
        await company.update(
          {
            size_band: company.size_band || payload.company_size,
            lifecycle_stage: company.lifecycle_stage || 'lead',
            notes: buildCompanyNote(payload),
          },
          { transaction },
        );
      }

      let contact = await db.contacts.findOne({
        where: {
          ...scope,
          email: { [Op.iLike]: payload.email },
        },
        transaction,
      });

      const contactPayload = {
        first_name: firstName,
        last_name: lastName,
        email: payload.email,
        phone: payload.phone || null,
        whatsapp: payload.preferred_channel === 'whatsapp' ? payload.phone || null : null,
        preferred_channel: payload.preferred_channel,
        allow_email: payload.preferred_channel === 'email',
        allow_sms: payload.preferred_channel === 'phone',
        allow_whatsapp: payload.preferred_channel === 'whatsapp',
        lifecycle_stage: 'lead',
        notes: buildContactNote(payload),
      };

      if (!contact) {
        contact = await db.contacts.create(contactPayload, { transaction });
        await attachScope(contact, tenant, organization, transaction);
      } else {
        await contact.update(contactPayload, { transaction });
      }

      await contact.setCompany(company, { transaction });

      let lead = await db.leads.findOne({
        where: {
          ...scope,
          email: { [Op.iLike]: payload.email },
          company_name: { [Op.iLike]: payload.company_name },
          status: {
            [Op.in]: ['new', 'qualified'],
          },
        },
        order: [['updatedAt', 'DESC']],
        transaction,
      });

      const leadPayload = {
        full_name: payload.full_name,
        email: payload.email,
        phone: payload.phone || null,
        company_name: payload.company_name,
        message: buildLeadMessage(payload, catalog),
        source: 'website_form',
        score,
        status: leadStatus,
        captured_at: new Date(),
      };

      if (!lead) {
        lead = await db.leads.create(leadPayload, { transaction });
        await attachScope(lead, tenant, organization, transaction);
      } else {
        await lead.update(leadPayload, { transaction });
      }

      await lead.setService_interest(catalog.service, { transaction });

      let deal = await db.deals.findOne({
        where: {
          ...scope,
          companyId: company.id,
          service_lineId: catalog.serviceLine.id,
          status: 'open',
        },
        order: [['updatedAt', 'DESC']],
        transaction,
      });

      const dealPayload = {
        name: `Découverte ${payload.company_name} · ${catalog.serviceNameFr}`,
        amount: getBudgetEstimate(payload.budget_range),
        currency: 'USD',
        expected_close_at: addDays(14),
        status: 'open',
        notes: buildDealNote(payload, score, lead, catalog),
      };

      if (!deal) {
        deal = await db.deals.create(dealPayload, { transaction });
        await attachScope(deal, tenant, organization, transaction);
      } else {
        await deal.update(dealPayload, { transaction });
      }

      await deal.setCompany(company, { transaction });
      await deal.setPrimary_contact(contact, { transaction });
      await deal.setService_line(catalog.serviceLine, { transaction });
      await deal.setPipeline(pipeline, { transaction });

      if (firstStage) {
        await deal.setStage(firstStage, { transaction });
      }

      await transaction.commit();

      res.status(200).send({
        success: true,
        reference: `TSC-${lead.id.split('-')[0].toUpperCase()}`,
        leadId: lead.id,
        dealId: deal.id,
        score,
        status: leadStatus,
        stageName: firstStage?.name || 'Nouveau besoin',
        pipelineName: pipeline?.name || 'Pipeline commercial TSC',
        templateSuggestion: catalog.projectTemplate?.name || catalog.templateName,
      });
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }),
);

router.use('/', require('../helpers').commonErrorHandler);

module.exports = router;
