// app/api/virements-salaires/export-sepa/route.ts
import { NextResponse } from "next/server";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";

export const dynamic = 'force-dynamic';
export const revalidate = 0;
export const runtime = 'nodejs';

// Fonction pour générer un identifiant de message unique
function generateMessageId(): string {
  const now = new Date();
  const timestamp = now.toISOString().replace(/[-:T.]/g, '').slice(0, 14);
  const random = Math.random().toString(36).substring(2, 8).toUpperCase();
  return `MSG${timestamp}${random}`;
}

// Fonction pour formater la date au format ISO (YYYY-MM-DD)
function formatDateISO(date: Date = new Date()): string {
  return date.toISOString().split('T')[0];
}

// Fonction pour formater la date-heure ISO 8601
function formatDateTimeISO(date: Date = new Date()): string {
  return date.toISOString();
}

// Fonction pour nettoyer et limiter les caractères XML
function sanitizeXML(str: string, maxLength?: number): string {
  let cleaned = str
    .replace(/[<>&'"]/g, '') // Supprimer les caractères XML problématiques
    .replace(/[^\x20-\x7E\xA0-\xFF]/g, '') // Garder uniquement les caractères imprimables
    .trim();
  
  if (maxLength && cleaned.length > maxLength) {
    cleaned = cleaned.substring(0, maxLength);
  }
  
  return cleaned;
}

// Fonction pour valider un IBAN avec checksum (modulo 97)
function isValidIBAN(iban: string): boolean {
  const cleaned = iban.replace(/\s/g, '').toUpperCase();
  
  // Vérification du format de base
  if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(cleaned) || cleaned.length < 15 || cleaned.length > 34) {
    return false;
  }
  
  // Vérification du checksum (modulo 97)
  // Déplacer les 4 premiers caractères à la fin
  const rearranged = cleaned.slice(4) + cleaned.slice(0, 4);
  
  // Convertir les lettres en chiffres (A=10, B=11, ..., Z=35)
  const numeric = rearranged.split('').map(char => {
    const code = char.charCodeAt(0);
    return code >= 65 && code <= 90 ? (code - 55).toString() : char;
  }).join('');
  
  // Calculer le modulo 97
  let remainder = '';
  for (let i = 0; i < numeric.length; i++) {
    remainder += numeric[i];
    if (remainder.length > 9) {
      remainder = (parseInt(remainder, 10) % 97).toString();
    }
  }
  
  return parseInt(remainder, 10) % 97 === 1;
}

// Fonction pour extraire le BIC depuis l'IBAN si non fourni (approximatif)
function extractBICFromIBAN(iban: string): string | null {
  // Cette fonction est approximative - dans un cas réel, il faudrait une base de données BIC
  // Pour la France, on peut extraire le code banque mais pas le BIC complet
  return null;
}

// Fonction pour formater une période (YYYY-MM-DD) en texte (ex: "Octobre 2025")
function formatPeriod(periodStart: string | null): string {
  if (!periodStart) return '';
  
  try {
    const date = new Date(periodStart);
    const monthNames = [
      'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
      'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'
    ];
    const month = monthNames[date.getMonth()];
    const year = date.getFullYear();
    return `${month} ${year}`;
  } catch {
    return '';
  }
}

type PayslipDetail = {
  id: string;
  net_after_withholding: number;
  contract_id: string;
  period_start: string | null;
  cddu_contracts: {
    contract_number: string;
    employee_id: string;
    salaries: {
      id: string;
      nom: string;
      prenom: string;
      iban: string | null;
      bic: string | null;
    };
  };
};

type GroupedPayment = {
  employeeId: string;
  employeeName: string;
  iban: string;
  bic: string;
  totalAmount: number;
  payslipIds: string[];
  references: string[];
  period?: string; // Format "Octobre 2025"
};

export async function POST(req: Request) {
  try {
    const supabase = createRouteHandlerClient({ cookies });
    const { data: { session } } = await supabase.auth.getSession();
    
    if (!session) {
      return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
    }

    const body = await req.json();
    const { payslipIds, organizationId } = body;

    if (!payslipIds || !Array.isArray(payslipIds) || payslipIds.length === 0) {
      return NextResponse.json({ error: 'payslipIds_required' }, { status: 400 });
    }

    if (!organizationId) {
      return NextResponse.json({ error: 'organizationId_required' }, { status: 400 });
    }

    // Récupérer les informations de l'organisation (donneur d'ordre)
    const { data: orgData, error: orgError } = await supabase
      .from('organizations')
      .select(`
        id,
        name,
        structure_api,
        organization_details(
          iban,
          bic,
          code_employeur
        )
      `)
      .eq('id', organizationId)
      .single();

    if (orgError || !orgData) {
      return NextResponse.json({ error: 'organization_not_found' }, { status: 404 });
    }

    const orgDetails = Array.isArray(orgData.organization_details)
      ? orgData.organization_details[0]
      : orgData.organization_details;

    // Vérifier que l'organisation a un IBAN
    if (!orgDetails?.iban) {
      return NextResponse.json({ 
        error: 'missing_organization_iban',
        message: 'L\'IBAN de votre structure n\'est pas renseigné' 
      }, { status: 400 });
    }

    // Récupérer les détails des payslips avec les informations des salariés
    const { data: payslips, error: payslipsError } = await supabase
      .from('payslips')
      .select(`
        id,
        net_after_withholding,
        contract_id,
        period_start,
        cddu_contracts!inner(
          contract_number,
          employee_id,
          salaries!inner(
            id,
            nom,
            prenom,
            iban,
            bic
          )
        )
      `)
      .in('id', payslipIds)
      .eq('organization_id', organizationId);

    if (payslipsError) {
      console.error('Error fetching payslips:', payslipsError);
      return NextResponse.json({ error: 'database_error' }, { status: 500 });
    }

    if (!payslips || payslips.length === 0) {
      return NextResponse.json({ error: 'no_payslips_found' }, { status: 404 });
    }

    // Regrouper les paiements par salarié
    const paymentsByEmployee = new Map<string, GroupedPayment>();
    const employeesWithoutIBAN: string[] = [];

    for (const payslip of payslips as any[]) {
      const contract = payslip.cddu_contracts;
      const salary = contract.salaries;
      
      const employeeId = salary.id;
      const employeeName = `${salary.nom || ''} ${salary.prenom || ''}`.trim();
      const iban = salary.iban;
      const bic = salary.bic;
      const amount = parseFloat(payslip.net_after_withholding || 0);
      const periodFormatted = formatPeriod(payslip.period_start);
      
      // Vérifier si l'IBAN est présent et valide
      if (!iban || !isValidIBAN(iban)) {
        if (!employeesWithoutIBAN.includes(employeeName)) {
          employeesWithoutIBAN.push(employeeName);
        }
        continue; // Exclure ce payslip
      }

      // Regrouper les paiements par employé
      if (paymentsByEmployee.has(employeeId)) {
        const existing = paymentsByEmployee.get(employeeId)!;
        existing.totalAmount += amount;
        existing.payslipIds.push(payslip.id);
        existing.references.push(contract.contract_number);
        // Mettre à jour la période si elle n'est pas encore définie
        if (!existing.period && periodFormatted) {
          existing.period = periodFormatted;
        }
      } else {
        paymentsByEmployee.set(employeeId, {
          employeeId,
          employeeName,
          iban: iban.replace(/\s/g, ''),
          bic: bic || '', // Le BIC peut être déduit par la banque si vide
          totalAmount: amount,
          payslipIds: [payslip.id],
          references: [contract.contract_number],
          period: periodFormatted
        });
      }
    }

    // Si tous les salariés n'ont pas d'IBAN, retourner une erreur
    if (paymentsByEmployee.size === 0) {
      return NextResponse.json({
        error: 'no_valid_payments',
        message: 'Aucun salarié n\'a d\'IBAN valide',
        employeesWithoutIBAN
      }, { status: 400 });
    }

    // Calculer le montant total et le nombre de transactions
    const payments = Array.from(paymentsByEmployee.values());
    const totalAmount = payments.reduce((sum, p) => sum + p.totalAmount, 0);
    const numberOfTransactions = payments.length;

    // Générer le fichier SEPA XML (pain.001.001.03)
    const messageId = generateMessageId();
    const creationDateTime = formatDateTimeISO();
    const requestedExecutionDate = formatDateISO(); // Aujourd'hui ou date souhaitée
    const debtorIBAN = orgDetails.iban.replace(/\s/g, '');
    const debtorBIC = orgDetails.bic || '';
    const debtorName = sanitizeXML(orgData.structure_api || orgData.name, 70);

    let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
    xml += '<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n';
    xml += '  <CstmrCdtTrfInitn>\n';
    
    // Group Header
    xml += '    <GrpHdr>\n';
    xml += `      <MsgId>${messageId}</MsgId>\n`;
    xml += `      <CreDtTm>${creationDateTime}</CreDtTm>\n`;
    xml += `      <NbOfTxs>${numberOfTransactions}</NbOfTxs>\n`;
    xml += `      <CtrlSum>${totalAmount.toFixed(2)}</CtrlSum>\n`;
    xml += '      <InitgPty>\n';
    xml += `        <Nm>${debtorName}</Nm>\n`;
    xml += '      </InitgPty>\n';
    xml += '    </GrpHdr>\n';
    
    // Payment Information
    xml += '    <PmtInf>\n';
    xml += `      <PmtInfId>${messageId}-001</PmtInfId>\n`;
    xml += '      <PmtMtd>TRF</PmtMtd>\n';
    xml += `      <NbOfTxs>${numberOfTransactions}</NbOfTxs>\n`;
    xml += `      <CtrlSum>${totalAmount.toFixed(2)}</CtrlSum>\n`;
    xml += '      <PmtTpInf>\n';
    xml += '        <SvcLvl>\n';
    xml += '          <Cd>SEPA</Cd>\n';
    xml += '        </SvcLvl>\n';
    xml += '        <CtgyPurp>\n';
    xml += '          <Cd>SALA</Cd>\n';
    xml += '        </CtgyPurp>\n';
    xml += '      </PmtTpInf>\n';
    xml += `      <ReqdExctnDt>${requestedExecutionDate}</ReqdExctnDt>\n`;
    xml += '      <Dbtr>\n';
    xml += `        <Nm>${debtorName}</Nm>\n`;
    xml += '      </Dbtr>\n';
    xml += '      <DbtrAcct>\n';
    xml += '        <Id>\n';
    xml += `          <IBAN>${debtorIBAN}</IBAN>\n`;
    xml += '        </Id>\n';
    xml += '      </DbtrAcct>\n';
    if (debtorBIC) {
      xml += '      <DbtrAgt>\n';
      xml += '        <FinInstnId>\n';
      xml += `          <BIC>${debtorBIC}</BIC>\n`;
      xml += '        </FinInstnId>\n';
      xml += '      </DbtrAgt>\n';
    }
    xml += '      <ChrgBr>SLEV</ChrgBr>\n';
    
    // Credit Transfer Transaction Information (une par bénéficiaire)
    payments.forEach((payment, index) => {
      const endToEndId = `${messageId}-${String(index + 1).padStart(3, '0')}`;
      const creditorName = sanitizeXML(payment.employeeName, 70);
      
      // Format: "Nom organisation - Période" (ex: "Atelier Moz - Octobre 2025")
      // Limite SEPA: 140 caractères pour le libellé
      const orgName = debtorName;
      const periodText = payment.period || 'Salaire';
      
      // Calculer l'espace disponible pour le nom de l'organisation
      // Format: "OrgName - Période" → " - " = 3 caractères
      const separator = ' - ';
      const maxOrgNameLength = 140 - separator.length - periodText.length;
      
      // Tronquer le nom de l'organisation si nécessaire
      const truncatedOrgName = orgName.length > maxOrgNameLength 
        ? orgName.substring(0, maxOrgNameLength - 3) + '...'
        : orgName;
      
      const remittanceInfo = sanitizeXML(`${truncatedOrgName}${separator}${periodText}`, 140);
      
      xml += '      <CdtTrfTxInf>\n';
      xml += '        <PmtId>\n';
      xml += `          <EndToEndId>${endToEndId}</EndToEndId>\n`;
      xml += '        </PmtId>\n';
      xml += '        <Amt>\n';
      xml += `          <InstdAmt Ccy="EUR">${payment.totalAmount.toFixed(2)}</InstdAmt>\n`;
      xml += '        </Amt>\n';
      if (payment.bic) {
        xml += '        <CdtrAgt>\n';
        xml += '          <FinInstnId>\n';
        xml += `            <BIC>${payment.bic}</BIC>\n`;
        xml += '          </FinInstnId>\n';
        xml += '        </CdtrAgt>\n';
      }
      xml += '        <Cdtr>\n';
      xml += `          <Nm>${creditorName}</Nm>\n`;
      xml += '        </Cdtr>\n';
      xml += '        <CdtrAcct>\n';
      xml += '          <Id>\n';
      xml += `            <IBAN>${payment.iban}</IBAN>\n`;
      xml += '          </Id>\n';
      xml += '        </CdtrAcct>\n';
      xml += '        <RmtInf>\n';
      xml += `          <Ustrd>${remittanceInfo}</Ustrd>\n`;
      xml += '        </RmtInf>\n';
      xml += '      </CdtTrfTxInf>\n';
    });
    
    xml += '    </PmtInf>\n';
    xml += '  </CstmrCdtTrfInitn>\n';
    xml += '</Document>';

    // Retourner le XML avec les métadonnées
    return NextResponse.json({
      success: true,
      xml,
      metadata: {
        messageId,
        numberOfTransactions,
        totalAmount: totalAmount.toFixed(2),
        currency: 'EUR',
        executionDate: requestedExecutionDate,
        debtorName,
        employeesWithoutIBAN: employeesWithoutIBAN.length > 0 ? employeesWithoutIBAN : undefined,
        includedPayments: payments.length,
        excludedPayments: employeesWithoutIBAN.length
      }
    });

  } catch (error) {
    console.error('Error generating SEPA XML:', error);
    return NextResponse.json({ 
      error: 'internal_server_error',
      message: error instanceof Error ? error.message : 'Unknown error'
    }, { status: 500 });
  }
}
