<?php

declare(strict_types=1);

namespace ApiModel\Infrastructure\Persistence\Repositories;

use ApiModel\Domain\Repository\JournalRepositoryInterface;
use ApiModel\Infrastructure\Persistence\Database;
use PDO;

final class PdoJournalRepository implements JournalRepositoryInterface
{
    public function __construct(private readonly PDO $pdo) {}

    public function findEntryById(string $id): ?array
    {
        $st = $this->pdo->prepare('SELECT * FROM journal_entries WHERE id = ?');
        $st->execute([$id]);
        $row = $st->fetch();

        return $row !== false ? $row : null;
    }

    public function listEntriesByCompany(string $companyId, ?string $branchId = null): array
    {
        $sql = 'SELECT * FROM journal_entries WHERE company_id = ?';
        $params = [$companyId];

        if ($branchId !== null && $branchId !== '') {
            $sql .= ' AND branch_id = ?';
            $params[] = $branchId;
        }

        $sql .= ' ORDER BY entry_date DESC, entry_number DESC';

        $st = $this->pdo->prepare($sql);
        $st->execute($params);

        return $st->fetchAll();
    }

    public function listLinesByEntry(string $entryId): array
    {
        $st = $this->pdo->prepare(
            'SELECT jl.*, ca.code AS account_code, ca.name AS account_name
             FROM journal_lines jl
             INNER JOIN chart_accounts ca ON ca.id = jl.chart_account_id
             WHERE jl.journal_entry_id = ?
             ORDER BY jl.line_order ASC, jl.id ASC'
        );
        $st->execute([$entryId]);

        return $st->fetchAll();
    }

    public function nextEntryNumber(string $companyId): string
    {
        $st = $this->pdo->prepare(
            'SELECT entry_number FROM journal_entries WHERE company_id = ? ORDER BY created_at DESC LIMIT 1'
        );
        $st->execute([$companyId]);
        $last = $st->fetchColumn();
        if ($last === false || ! is_string($last)) {
            return 'JE-000001';
        }

        if (preg_match('/(\d+)$/', $last, $m)) {
            $next = (int) $m[1] + 1;

            return 'JE-' . str_pad((string) $next, 6, '0', STR_PAD_LEFT);
        }

        return 'JE-' . Database::uuid();
    }

    public function insertEntry(
        string $id,
        string $companyId,
        ?string $branchId,
        string $entryNumber,
        string $entryDate,
        ?string $description,
        string $status,
        string $operatingCurrency,
        string $functionalCurrency,
        string $exchangeRate,
        ?string $createdBy
    ): void {
        $this->pdo->prepare(
            'INSERT INTO journal_entries
             (id, company_id, branch_id, entry_number, entry_date, description, status,
              operating_currency, functional_currency, exchange_rate, created_by)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
        )->execute([
            $id, $companyId, $branchId, $entryNumber, $entryDate, $description, $status,
            $operatingCurrency, $functionalCurrency, $exchangeRate, $createdBy,
        ]);
    }

    public function updateEntry(
        string $id,
        ?string $branchId,
        string $entryDate,
        ?string $description,
        string $operatingCurrency,
        string $functionalCurrency,
        string $exchangeRate
    ): void {
        $this->pdo->prepare(
            'UPDATE journal_entries
             SET branch_id = ?, entry_date = ?, description = ?,
                 operating_currency = ?, functional_currency = ?, exchange_rate = ?,
                 updated_at = CURRENT_TIMESTAMP
             WHERE id = ?'
        )->execute([
            $branchId, $entryDate, $description,
            $operatingCurrency, $functionalCurrency, $exchangeRate, $id,
        ]);
    }

    public function updateEntryStatus(string $id, string $status): void
    {
        $this->pdo->prepare(
            'UPDATE journal_entries SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?'
        )->execute([$status, $id]);
    }

    public function deleteEntry(string $id): void
    {
        $this->pdo->prepare('DELETE FROM journal_entries WHERE id = ?')->execute([$id]);
    }

    public function deleteLinesByEntry(string $entryId): void
    {
        $this->pdo->prepare('DELETE FROM journal_lines WHERE journal_entry_id = ?')->execute([$entryId]);
    }

    public function insertLines(string $entryId, array $lines): void
    {
        $st = $this->pdo->prepare(
            'INSERT INTO journal_lines
             (id, journal_entry_id, chart_account_id, line_order, description, debit, credit, debit_functional, credit_functional)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
        );

        foreach ($lines as $index => $line) {
            $st->execute([
                Database::uuid(),
                $entryId,
                $line['chart_account_id'],
                $line['line_order'] ?? $index,
                $line['description'] ?? null,
                $line['debit'],
                $line['credit'],
                $line['debit_functional'],
                $line['credit_functional'],
            ]);
        }
    }
}
