<?php
class PaymentReceipt
{
    public static function ensureTable(): void
    {
        $pdo = Database::pdo();
        if (Database::driver() === 'mysql') {
            $pdo->exec("CREATE TABLE IF NOT EXISTS payment_receipts (
                id INT AUTO_INCREMENT PRIMARY KEY,
                charge_id INT NOT NULL,
                recipient_type VARCHAR(20) NOT NULL,
                recipient_id INT NOT NULL,
                receipt_code VARCHAR(24) NOT NULL,
                company_name VARCHAR(180) NOT NULL,
                company_document VARCHAR(40) NULL,
                logo_path VARCHAR(255) NULL,
                client_name VARCHAR(180) NOT NULL,
                service_title VARCHAR(180) NOT NULL,
                amount DECIMAL(10,2) NOT NULL DEFAULT 0,
                payment_method VARCHAR(160) NULL,
                paid_at DATETIME NOT NULL,
                created_at DATETIME NOT NULL,
                UNIQUE KEY uq_payment_receipt_charge (charge_id),
                UNIQUE KEY uq_payment_receipt_code (receipt_code),
                INDEX idx_payment_receipt_actor (recipient_type, recipient_id),
                CONSTRAINT fk_payment_receipt_charge FOREIGN KEY (charge_id) REFERENCES payment_charges(id) ON DELETE CASCADE
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
        } else {
            $pdo->exec("CREATE TABLE IF NOT EXISTS payment_receipts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                charge_id INTEGER NOT NULL,
                recipient_type TEXT NOT NULL,
                recipient_id INTEGER NOT NULL,
                receipt_code TEXT NOT NULL,
                company_name TEXT NOT NULL,
                company_document TEXT NULL,
                logo_path TEXT NULL,
                client_name TEXT NOT NULL,
                service_title TEXT NOT NULL,
                amount REAL NOT NULL DEFAULT 0,
                payment_method TEXT NULL,
                paid_at TEXT NOT NULL,
                created_at TEXT NOT NULL,
                FOREIGN KEY (charge_id) REFERENCES payment_charges(id) ON DELETE CASCADE,
                UNIQUE (charge_id),
                UNIQUE (receipt_code)
            )");
            $pdo->exec('CREATE INDEX IF NOT EXISTS idx_payment_receipt_actor ON payment_receipts(recipient_type, recipient_id)');
        }
 
        if (Database::driver() === 'mysql') {
            $pdo->exec("CREATE TABLE IF NOT EXISTS manual_payment_receipts (
                id INT AUTO_INCREMENT PRIMARY KEY,
                recipient_type VARCHAR(20) NOT NULL,
                recipient_id INT NOT NULL,
                receipt_code VARCHAR(24) NOT NULL,
                company_name VARCHAR(180) NOT NULL,
                company_document VARCHAR(40) NULL,
                logo_path VARCHAR(255) NULL,
                client_name VARCHAR(180) NOT NULL,
                service_title VARCHAR(180) NOT NULL,
                amount DECIMAL(10,2) NOT NULL DEFAULT 0,
                payment_method VARCHAR(160) NULL,
                paid_at DATETIME NOT NULL,
                created_at DATETIME NOT NULL,
                UNIQUE KEY uq_manual_payment_receipt_code (receipt_code),
                INDEX idx_manual_payment_receipt_actor (recipient_type, recipient_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
        } else {
            $pdo->exec("CREATE TABLE IF NOT EXISTS manual_payment_receipts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                recipient_type TEXT NOT NULL,
                recipient_id INTEGER NOT NULL,
                receipt_code TEXT NOT NULL UNIQUE,
                company_name TEXT NOT NULL,
                company_document TEXT NULL,
                logo_path TEXT NULL,
                client_name TEXT NOT NULL,
                service_title TEXT NOT NULL,
                amount REAL NOT NULL DEFAULT 0,
                payment_method TEXT NULL,
                paid_at TEXT NOT NULL,
                created_at TEXT NOT NULL
            )");
            $pdo->exec('CREATE INDEX IF NOT EXISTS idx_manual_payment_receipt_actor ON manual_payment_receipts(recipient_type, recipient_id)');
        }
    }

    public static function ensureForCharge(int $chargeId): ?array
    {
        Database::migrate();
        self::ensureTable();
        $existing = self::findByCharge($chargeId);
        if ($existing) return $existing;

        $stmt = Database::pdo()->prepare("SELECT c.*, b.name AS bank_name
            FROM payment_charges c
            INNER JOIN payment_banks b ON b.id=c.bank_id
            WHERE c.id=? AND c.status='paid' LIMIT 1");
        $stmt->execute([$chargeId]);
        $charge = $stmt->fetch();
        if (!$charge) return null;

        $actor = Communication::actorByTypeAndId((string)$charge['recipient_type'], (int)$charge['recipient_id']);
        if (!$actor) return null;
        $settings = Settings::safeAll();
        $companyName = trim((string)($settings['receipt_company_name'] ?? ''));
        if ($companyName === '') $companyName = trim((string)($settings['site_name'] ?? 'CFC CONTROLE'));
        $document = trim((string)($settings['receipt_company_document'] ?? ''));
        $logoPath = trim((string)($settings['logo_path'] ?? ''));
        $paidAt = !empty($charge['paid_at']) ? (string)$charge['paid_at'] : date('Y-m-d H:i:s');

        $code = self::newCode();

        $insert = Database::pdo()->prepare('INSERT INTO payment_receipts
            (charge_id,recipient_type,recipient_id,receipt_code,company_name,company_document,logo_path,client_name,service_title,amount,payment_method,paid_at,created_at)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
        $insert->execute([
            $chargeId,
            normalize_internal_actor_type((string)$charge['recipient_type']),
            (int)$charge['recipient_id'],
            $code,
            $companyName,
            $document !== '' ? $document : null,
            $logoPath !== '' ? $logoPath : null,
            (string)($actor['name'] ?? 'Cliente'),
            (string)$charge['title'],
            (float)$charge['amount'],
            (string)($charge['bank_name'] ?? ''),
            $paidAt,
            date('Y-m-d H:i:s'),
        ]);
        return self::findByCharge($chargeId);
    }

    public static function removeForCharge(int $chargeId): void
    {
        self::ensureTable();
        $stmt = Database::pdo()->prepare('DELETE FROM payment_receipts WHERE charge_id=?');
        $stmt->execute([$chargeId]);
    }

    public static function findByCharge(int $chargeId): ?array
    {
        self::ensureTable();
        $stmt = Database::pdo()->prepare('SELECT * FROM payment_receipts WHERE charge_id=? LIMIT 1');
        $stmt->execute([$chargeId]);
        return $stmt->fetch() ?: null;
    }

    private static function newCode(): string
    {
        do {
            $code = strtoupper(bin2hex(random_bytes(6)));
            $a = Database::pdo()->prepare('SELECT COUNT(*) FROM payment_receipts WHERE receipt_code=?');
            $a->execute([$code]);
            $b = Database::pdo()->prepare('SELECT COUNT(*) FROM manual_payment_receipts WHERE receipt_code=?');
            $b->execute([$code]);
        } while ((int)$a->fetchColumn() > 0 || (int)$b->fetchColumn() > 0);
        return $code;
    }

    public static function createManualForStudent(array $data): array
    {
        Database::migrate();
        self::ensureTable();
        $studentId = (int)($data['student_id'] ?? 0);
        $student = User::find($studentId);
        if (!$student || ($student['role'] ?? '') !== 'student') {
            return ['ok'=>false,'message'=>'Selecione um aluno válido.'];
        }
        $amount = max(0, parse_money_br($data['amount'] ?? 0));
        if ($amount <= 0) return ['ok'=>false,'message'=>'Informe um valor maior que zero.'];
        $paidDate = trim((string)($data['paid_date'] ?? date('Y-m-d')));
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $paidDate)) return ['ok'=>false,'message'=>'Informe uma data de pagamento válida.'];
        $service = trim((string)($data['service_title'] ?? '')) ?: 'Pagamento registrado pelo administrador';
        $method = trim((string)($data['payment_method'] ?? '')) ?: 'Pagamento informado pelo administrador';
        $settings = Settings::safeAll();
        $companyName = trim((string)($settings['receipt_company_name'] ?? '')) ?: (trim((string)($settings['site_name'] ?? '')) ?: 'CFC CONTROLE');
        $document = trim((string)($settings['receipt_company_document'] ?? ''));
        $logoPath = trim((string)($settings['logo_path'] ?? ''));
        $paidAt = $paidDate . ' ' . date('H:i:s');
        $code = self::newCode();
        $stmt = Database::pdo()->prepare('INSERT INTO manual_payment_receipts
            (recipient_type,recipient_id,receipt_code,company_name,company_document,logo_path,client_name,service_title,amount,payment_method,paid_at,created_at)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
        $stmt->execute(['student',$studentId,$code,$companyName,$document!==''?$document:null,$logoPath!==''?$logoPath:null,(string)$student['name'],$service,$amount,$method,$paidAt,date('Y-m-d H:i:s')]);
        $receipt = self::findByCode($code);
        return ['ok'=>true,'message'=>'Comprovante avulso gerado com sucesso.','receipt'=>$receipt];
    }

    public static function manualAll(): array
    {
        self::ensureTable();
        $stmt = Database::pdo()->query("SELECT r.*, u.whatsapp AS recipient_whatsapp, 'manual' AS receipt_source
            FROM manual_payment_receipts r LEFT JOIN users u ON r.recipient_type='student' AND u.id=r.recipient_id
            ORDER BY r.paid_at DESC, r.id DESC LIMIT 200");
        return $stmt->fetchAll();
    }

    public static function findByCode(string $code): ?array
    {
        self::ensureTable();
        $code = strtoupper(preg_replace('/[^A-F0-9]/i', '', $code) ?? '');
        if (strlen($code) < 8) return null;
        $stmt = Database::pdo()->prepare("SELECT r.*, 'charge' AS receipt_source FROM payment_receipts r WHERE receipt_code=? LIMIT 1");
        $stmt->execute([$code]);
        $row=$stmt->fetch();
        if($row) return $row;
        $stmt = Database::pdo()->prepare("SELECT r.*, 'manual' AS receipt_source FROM manual_payment_receipts r WHERE receipt_code=? LIMIT 1");
        $stmt->execute([$code]);
        return $stmt->fetch() ?: null;
    }

    public static function forActor(string $type, int $id): array
    {
        self::ensureTable();
        $type = normalize_internal_actor_type($type);
        $stmt = Database::pdo()->prepare("SELECT r.*, c.status AS charge_status, 'charge' AS receipt_source
            FROM payment_receipts r INNER JOIN payment_charges c ON c.id=r.charge_id
            WHERE r.recipient_type=? AND r.recipient_id=? AND c.status='paid'");
        $stmt->execute([$type, $id]);
        $rows=$stmt->fetchAll();
        $manual=Database::pdo()->prepare("SELECT r.*, 'paid' AS charge_status, 'manual' AS receipt_source
            FROM manual_payment_receipts r WHERE r.recipient_type=? AND r.recipient_id=?");
        $manual->execute([$type,$id]);
        $rows=array_merge($rows,$manual->fetchAll());
        usort($rows, static fn(array $a,array $b): int => strcmp((string)($b['paid_at']??''),(string)($a['paid_at']??'')) ?: ((int)($b['id']??0)<=> (int)($a['id']??0)));
        return $rows;
    }

    public static function svg(array $receipt): string
    {
        $esc = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES | ENT_XML1, 'UTF-8');
        $company = self::fitText((string)($receipt['company_name'] ?? 'CFC CONTROLE'), 38);
        $document = trim((string)($receipt['company_document'] ?? ''));
        $client = self::fitText((string)($receipt['client_name'] ?? 'Cliente'), 48);
        $service = self::fitText((string)($receipt['service_title'] ?? 'Pagamento'), 30);
        $method = self::fitText((string)($receipt['payment_method'] ?? ''), 28);
        $amount = format_money_br((float)($receipt['amount'] ?? 0));
        $paidDate = !empty($receipt['paid_at']) ? date('d/m/Y', strtotime((string)$receipt['paid_at'])) : date('d/m/Y');
        $paidTime = !empty($receipt['paid_at']) ? date('H:i', strtotime((string)$receipt['paid_at'])) : date('H:i');
        $code = (string)($receipt['receipt_code'] ?? '');
        $generated = !empty($receipt['created_at']) ? date('d/m/Y', strtotime((string)$receipt['created_at'])) : date('d/m/Y');
        $logo = self::logoDataUri((string)($receipt['logo_path'] ?? ''));
        $logoMarkup = $logo !== ''
            ? '<image href="'.$esc($logo).'" x="842" y="86" width="145" height="145" preserveAspectRatio="xMidYMid meet"/>'
            : '<rect x="842" y="86" width="145" height="145" rx="28" fill="#fff"/><text x="914" y="173" text-anchor="middle" font-size="44" font-weight="800" fill="#1769c2">CFC</text>';
        $documentLine = $document !== '' ? '<text x="105" y="232" font-size="22" font-weight="600" fill="#dbeafe">'.$esc($document).'</text>' : '';

        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            .'<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="1350" viewBox="0 0 1080 1350">'
            .'<defs><linearGradient id="head" x1="0" x2="1"><stop stop-color="#114f9e"/><stop offset="1" stop-color="#2388e6"/></linearGradient><filter id="s" x="-20%" y="-20%" width="140%" height="140%"><feDropShadow dx="0" dy="10" stdDeviation="14" flood-color="#15324f" flood-opacity=".16"/></filter></defs>'
            .'<rect width="1080" height="1350" fill="#eaf3ff"/>'
            .'<rect x="54" y="54" width="972" height="1242" rx="38" fill="#fff" filter="url(#s)"/>'
            .'<path d="M54 92a38 38 0 0 1 38-38h896a38 38 0 0 1 38 38v302H54z" fill="url(#head)"/>'
            .'<rect x="54" y="310" width="972" height="84" fill="#2388e6" opacity=".9"/>'
            .'<text x="105" y="150" font-family="Arial,sans-serif" font-size="42" font-weight="800" fill="#fff">COMPROVANTE DE PAGAMENTO</text>'
            .'<text x="105" y="190" font-family="Arial,sans-serif" font-size="29" font-weight="700" fill="#eaf4ff">'.$esc($company).'</text>'.$documentLine
            .'<rect x="105" y="258" width="350" height="60" rx="30" fill="#e8fff0"/>'
            .'<text x="280" y="298" text-anchor="middle" font-family="Arial,sans-serif" font-size="23" font-weight="800" fill="#16733a">✓ PAGAMENTO CONFIRMADO</text>'
            .$logoMarkup
            .self::svgBox(105, 438, 870, 125, 'Cliente', $client)
            .self::svgBox(105, 592, 420, 125, 'Produto / serviço', $service)
            .self::svgBox(555, 592, 420, 125, 'Valor pago', $amount)
            .self::svgBox(105, 746, 420, 125, 'Data do pagamento', $paidDate.' às '.$paidTime)
            .self::svgBox(555, 746, 420, 125, 'Forma de pagamento', $method !== '' ? $method : 'Não informada')
            .self::svgBox(105, 900, 870, 125, 'Responsável', $company)
            .'<line x1="105" y1="1080" x2="975" y2="1080" stroke="#cbd5e1" stroke-width="2" stroke-dasharray="12 12"/>'
            .'<text x="105" y="1140" font-family="Arial,sans-serif" font-size="21" font-weight="700" fill="#64748b">Código do comprovante</text>'
            .'<text x="105" y="1184" font-family="Arial,sans-serif" font-size="31" font-weight="800" fill="#172033">'.$esc($code).'</text>'
            .'<text x="975" y="1184" text-anchor="end" font-family="Arial,sans-serif" font-size="21" font-weight="700" fill="#64748b">Gerado em '.$esc($generated).'</text>'
            .'<text x="540" y="1252" text-anchor="middle" font-family="Arial,sans-serif" font-size="20" font-weight="700" fill="#64748b">Documento digital de confirmação do pagamento</text>'
            .'</svg>';
    }

    private static function svgBox(int $x, int $y, int $w, int $h, string $label, string $value): string
    {
        return '<rect x="'.$x.'" y="'.$y.'" width="'.$w.'" height="'.$h.'" rx="22" fill="#f8fafc" stroke="#d9e2ec" stroke-width="2"/>'
            .'<text x="'.($x+25).'" y="'.($y+38).'" font-family="Arial,sans-serif" font-size="20" font-weight="700" fill="#64748b">'.htmlspecialchars($label, ENT_QUOTES|ENT_XML1, 'UTF-8').'</text>'
            .'<text x="'.($x+25).'" y="'.($y+86).'" font-family="Arial,sans-serif" font-size="29" font-weight="800" fill="#172033">'.htmlspecialchars($value, ENT_QUOTES|ENT_XML1, 'UTF-8').'</text>';
    }

    private static function fitText(string $value, int $limit): string
    {
        $value = trim(preg_replace('/\s+/u', ' ', $value) ?? $value);
        if (function_exists('mb_strlen') && function_exists('mb_substr')) {
            return mb_strlen($value) > $limit ? rtrim(mb_substr($value, 0, max(1, $limit - 1))) . '…' : $value;
        }
        return strlen($value) > $limit ? rtrim(substr($value, 0, max(1, $limit - 3))) . '...' : $value;
    }

    private static function logoDataUri(string $relative): string
    {
        if ($relative === '') return '';
        $path = ROOT_PATH . '/' . ltrim($relative, '/');
        $realRoot = realpath(ROOT_PATH);
        $realPath = realpath($path);
        if (!$realRoot || !$realPath || !str_starts_with($realPath, $realRoot . DIRECTORY_SEPARATOR) || !is_file($realPath)) return '';
        $mime = function_exists('mime_content_type') ? (string)mime_content_type($realPath) : '';
        if (!str_starts_with($mime, 'image/')) return '';
        $data = file_get_contents($realPath);
        return $data === false ? '' : 'data:'.$mime.';base64,'.base64_encode($data);
    }
}
