<?php
// File: src/Services/AuditTrailExporter.php
// Version 1.0 - Audit trail export for HMRC / accountant review
// UK audit trail CSV export for HMRC and accountant review

namespace ComptaFlowUK\Services;

defined('ABSPATH') || exit;

class AuditTrailExporter
{
    /**
     * Export journal entries as CSV audit trail
     * UK format: UTF-8, GBP currency, Company Number in filename
     */
    public static function export(int $user_id, string $start_date, string $end_date): void
    {
        global $wpdb;
        $journal_table = $wpdb->prefix . 'cfuk_journal_entries';
        
        $entries = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $journal_table 
             WHERE created_by = %d 
             AND date_ecriture BETWEEN %s AND %s
             ORDER BY date_ecriture ASC, id ASC",
            $user_id, $start_date, $end_date
        ));
        
        if (empty($entries)) {
            wp_die('No journal entries found for this period.');
        }
        
        $content = self::generateCSVContent($entries, $start_date, $end_date);
        
        $company_settings = get_option('comptaflow_uk_company_settings', []);
        $company_number = $company_settings['company_number'] ?? 'UNKNOWN';
        
        // Filename: CompanyNumber_AuditTrail_YYYYMMDD-YYYYMMDD.csv
        $date_from = str_replace('-', '', $start_date);
        $date_to = str_replace('-', '', $end_date);
        $filename = $company_number . '_AuditTrail_' . $date_from . '-' . $date_to . '.csv';
        
        header('Content-Type: text/csv; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Pragma: no-cache');
        header('Expires: 0');
        
        // UTF-8 BOM for Excel compatibility
        echo "\xEF\xBB\xBF";
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV output, not HTML
        echo $content;
        exit;
    }
    
    /**
     * Generate CSV content with UK-standard headers
     */
    private static function generateCSVContent(array $entries, string $start_date, string $end_date): string
    {
        $lines = [];
        
        // CSV Header row
        $lines[] = implode(';', [
            'JournalCode',
            'JournalName',
            'EntryNumber',
            'EntryDate',
            'NominalCode',
            'NominalName',
            'SubAccountCode',
            'SubAccountName',
            'Reference',
            'ReferenceDate',
            'Description',
            'Debit',
            'Credit',
            'AllocationCode',
            'AllocationDate',
            'ValidationDate',
            'Currency'
        ]);
        
        // Group by reference for coherent numbering
        $pieces = [];
        foreach ($entries as $entry) {
            $piece_key = $entry->piece_ref ?: 'REF-' . $entry->transaction_id;
            if (!isset($pieces[$piece_key])) {
                $pieces[$piece_key] = [];
            }
            $pieces[$piece_key][] = $entry;
        }
        
        $entry_num = 1;
        
        foreach ($pieces as $piece_ref => $piece_entries) {
            foreach ($piece_entries as $entry) {
                $journal_code = self::getJournalCode($entry);
                $journal_name = self::getJournalName($journal_code);
                $nominal_name = self::getNominalName($entry->compte_num);
                
                $lines[] = implode(';', [
                    self::csvEscape($journal_code),
                    self::csvEscape($journal_name),
                    str_pad($entry_num, 8, '0', STR_PAD_LEFT),
                    gmdate('Y-m-d', strtotime($entry->date_ecriture)),
                    self::csvEscape($entry->compte_num),
                    self::csvEscape(substr($nominal_name, 0, 100)),
                    self::csvEscape($entry->partner_code ?: ''),
                    self::csvEscape($entry->partner_code ? substr($entry->partner_code, 0, 50) : ''),
                    self::csvEscape($piece_ref),
                    gmdate('Y-m-d', strtotime($entry->date_ecriture)),
                    self::csvEscape(substr(self::cleanText($entry->libelle), 0, 200)),
                    number_format((float)$entry->debit, 2, '.', ''),
                    number_format((float)$entry->credit, 2, '.', ''),
                    '',
                    '',
                    gmdate('Y-m-d', strtotime($entry->created_at)),
                    'GBP'
                ]);
            }
            
            $entry_num++;
        }
        
        return implode("\n", $lines);
    }
    
    /**
     * Determine journal code from entry source and nominal code
     */
    private static function getJournalCode($entry): string
    {
        if ($entry->source === 'BANK_RECO' || $entry->source === 'BANK_RECO_UNDO') {
            return 'CB';
        }
        
        if ($entry->source === 'MANUAL_OD') {
            return 'NL';
        }
        
        // Use stored journal_code if available
        if (!empty($entry->journal_code)) {
            return $entry->journal_code;
        }
        
        $code = $entry->compte_num;
        
        // Trade creditors (2100) or Purchase accounts (5xxx)
        if (strpos($code, '2100') === 0 || substr($code, 0, 1) === '5') {
            return 'PL';
        }
        
        // Trade debtors (1100) or Sales accounts (4xxx)
        if (strpos($code, '1100') === 0 || substr($code, 0, 1) === '4') {
            return 'SL';
        }
        
        // Bank (1200)
        if (strpos($code, '1200') === 0 || strpos($code, '1230') === 0) {
            return 'CB';
        }
        
        return 'NL';
    }
    
    /**
     * Journal name from code
     */
    private static function getJournalName(string $code): string
    {
        $labels = [
            'PL' => 'Purchase Ledger',
            'SL' => 'Sales Ledger',
            'CB' => 'Cash Book',
            'NL' => 'Nominal Ledger'
        ];
        return $labels[$code] ?? 'Nominal Ledger';
    }
    
    /**
     * Nominal account name lookup
     */
    private static function getNominalName(string $account_num): string
    {
        $account = ChartOfAccounts::getAccount($account_num);
        if ($account) {
            return $account['label'];
        }
        
        // Prefix search for generic accounts
        for ($len = strlen($account_num) - 1; $len >= 2; $len--) {
            $prefix = substr($account_num, 0, $len);
            $account = ChartOfAccounts::getAccount($prefix);
            if ($account) {
                return $account['label'];
            }
        }
        
        return 'Account ' . $account_num;
    }
    
    /**
     * Escape CSV field
     */
    private static function csvEscape(string $text): string
    {
        $text = self::cleanText($text);
        if (strpos($text, ',') !== false || strpos($text, '"') !== false || strpos($text, "\n") !== false) {
            return '"' . str_replace('"', '""', $text) . '"';
        }
        return $text;
    }
    
    /**
     * Clean text for export
     */
    private static function cleanText(string $text): string
    {
        $text = str_replace(["\n", "\r", "\t"], ' ', $text);
        $text = preg_replace('/\s+/', ' ', $text);
        return trim($text);
    }
    
    /**
     * Export as pipe-delimited TXT (alternative format for accountant review)
     */
    public static function exportPipeDelimited(int $user_id, string $start_date, string $end_date): void
    {
        global $wpdb;
        $journal_table = $wpdb->prefix . 'cfuk_journal_entries';
        
        $entries = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $journal_table 
             WHERE created_by = %d 
             AND date_ecriture BETWEEN %s AND %s
             ORDER BY date_ecriture ASC, id ASC",
            $user_id, $start_date, $end_date
        ));
        
        if (empty($entries)) {
            wp_die('No journal entries found for this period.');
        }
        
        $company_settings = get_option('comptaflow_uk_company_settings', []);
        $company_number = $company_settings['company_number'] ?? 'UNKNOWN';
        $date_to = str_replace('-', '', $end_date);
        $filename = $company_number . '_JournalExport_' . $date_to . '.txt';
        
        header('Content-Type: text/plain; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Pragma: no-cache');
        header('Expires: 0');
        
        // Header
        $lines = [];
        $lines[] = implode('|', [
            'JournalCode', 'JournalName', 'EntryNumber', 'EntryDate',
            'NominalCode', 'NominalName', 'SubAccountCode', 'SubAccountName',
            'Reference', 'ReferenceDate', 'Description', 'Debit', 'Credit',
            'AllocationCode', 'AllocationDate', 'ValidationDate', 'Currency'
        ]);
        
        $pieces = [];
        foreach ($entries as $entry) {
            $piece_key = $entry->piece_ref ?: 'REF-' . $entry->transaction_id;
            if (!isset($pieces[$piece_key])) { $pieces[$piece_key] = []; }
            $pieces[$piece_key][] = $entry;
        }
        
        $entry_num = 1;
        foreach ($pieces as $piece_ref => $piece_entries) {
            foreach ($piece_entries as $entry) {
                $journal_code = self::getJournalCode($entry);
                $lines[] = implode('|', [
                    $journal_code,
                    self::getJournalName($journal_code),
                    str_pad($entry_num, 8, '0', STR_PAD_LEFT),
                    gmdate('Ymd', strtotime($entry->date_ecriture)),
                    $entry->compte_num,
                    substr(self::getNominalName($entry->compte_num), 0, 100),
                    $entry->partner_code ?: '',
                    $entry->partner_code ? substr($entry->partner_code, 0, 50) : '',
                    str_replace('|', ' ', $piece_ref),
                    gmdate('Ymd', strtotime($entry->date_ecriture)),
                    substr(self::cleanText(str_replace('|', ' ', $entry->libelle)), 0, 200),
                    number_format((float)$entry->debit, 2, '.', ''),
                    number_format((float)$entry->credit, 2, '.', ''),
                    '', '',
                    gmdate('Ymd', strtotime($entry->created_at)),
                    'GBP'
                ]);
            }
            $entry_num++;
        }
        
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV output, not HTML
        echo implode("\n", $lines);
        exit;
    }
}
