<?php

declare(strict_types=1);

namespace ApiModel\Infrastructure\Persistence\Repositories;

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

final class PdoSalesAdjustmentRepository implements SalesAdjustmentRepositoryInterface
{
    public function __construct(private readonly PDO $pdo) {}

    public function listByCompany(string $companyId, string $type): array
    {
        $st = $this->pdo->prepare(
            'SELECT a.*, c.name as customer_name, i.invoice_number as reference_invoice_number
             FROM sales_adjustments a
             JOIN contacts c ON a.customer_id = c.id
             LEFT JOIN sales_invoices i ON a.reference_invoice_id = i.id
             WHERE a.company_id = ? AND a.adjustment_type = ?
             ORDER BY a.document_date DESC, a.created_at DESC'
        );
        $st->execute([$companyId, $type]);

        return $st->fetchAll(PDO::FETCH_ASSOC);
    }

    public function findById(string $id): ?array
    {
        $st = $this->pdo->prepare(
            'SELECT a.*, c.name as customer_name, i.invoice_number as reference_invoice_number, i.balance as reference_invoice_balance
             FROM sales_adjustments a
             JOIN contacts c ON a.customer_id = c.id
             LEFT JOIN sales_invoices i ON a.reference_invoice_id = i.id
             WHERE a.id = ?
             LIMIT 1'
        );
        $st->execute([$id]);
        $row = $st->fetch(PDO::FETCH_ASSOC);

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

    public function findLinesByAdjustmentId(string $adjustmentId): array
    {
        $st = $this->pdo->prepare(
            'SELECT l.*, a.code as account_code, a.name as account_name, t.name as tax_name, t.rate as tax_rate
             FROM sales_adjustment_lines l
             JOIN chart_accounts a ON l.chart_account_id = a.id
             LEFT JOIN tax_rates t ON l.tax_rate_id = t.id
             WHERE l.adjustment_id = ?
             ORDER BY l.line_order ASC'
        );
        $st->execute([$adjustmentId]);

        return $st->fetchAll(PDO::FETCH_ASSOC);
    }

    public function insert(array $adjustment): void
    {
        $st = $this->pdo->prepare(
            'INSERT INTO sales_adjustments (
                id, company_id, branch_id, customer_id, adjustment_type,
                document_number, document_date, reference_invoice_id, receivable_account_id,
                discount_amount, tax_amount, subtotal, total, status, journal_entry_id, notes, created_by
             ) VALUES (
                :id, :company_id, :branch_id, :customer_id, :adjustment_type,
                :document_number, :document_date, :reference_invoice_id, :receivable_account_id,
                :discount_amount, :tax_amount, :subtotal, :total, :status, :journal_entry_id, :notes, :created_by
             )'
        );
        $st->execute([
            'id' => $adjustment['id'],
            'company_id' => $adjustment['company_id'],
            'branch_id' => $adjustment['branch_id'] ?: null,
            'customer_id' => $adjustment['customer_id'],
            'adjustment_type' => $adjustment['adjustment_type'],
            'document_number' => $adjustment['document_number'],
            'document_date' => $adjustment['document_date'],
            'reference_invoice_id' => $adjustment['reference_invoice_id'] ?: null,
            'receivable_account_id' => $adjustment['receivable_account_id'],
            'discount_amount' => $adjustment['discount_amount'],
            'tax_amount' => $adjustment['tax_amount'],
            'subtotal' => $adjustment['subtotal'],
            'total' => $adjustment['total'],
            'status' => $adjustment['status'] ?: 'draft',
            'journal_entry_id' => $adjustment['journal_entry_id'] ?: null,
            'notes' => $adjustment['notes'] ?: null,
            'created_by' => $adjustment['created_by'] ?: null,
        ]);
    }

    public function update(string $id, array $adjustment): void
    {
        $st = $this->pdo->prepare(
            'UPDATE sales_adjustments SET
                branch_id = :branch_id,
                customer_id = :customer_id,
                receivable_account_id = :receivable_account_id,
                document_date = :document_date,
                reference_invoice_id = :reference_invoice_id,
                discount_amount = :discount_amount,
                tax_amount = :tax_amount,
                subtotal = :subtotal,
                total = :total,
                status = :status,
                notes = :notes,
                updated_at = CURRENT_TIMESTAMP
             WHERE id = :id'
        );
        $st->execute([
            'id' => $id,
            'branch_id' => $adjustment['branch_id'] ?: null,
            'customer_id' => $adjustment['customer_id'],
            'receivable_account_id' => $adjustment['receivable_account_id'],
            'document_date' => $adjustment['document_date'],
            'reference_invoice_id' => $adjustment['reference_invoice_id'] ?: null,
            'discount_amount' => $adjustment['discount_amount'],
            'tax_amount' => $adjustment['tax_amount'],
            'subtotal' => $adjustment['subtotal'],
            'total' => $adjustment['total'],
            'status' => $adjustment['status'] ?: 'draft',
            'notes' => $adjustment['notes'] ?: null,
        ]);
    }

    public function insertLines(string $adjustmentId, array $lines): void
    {
        $st = $this->pdo->prepare(
            'INSERT INTO sales_adjustment_lines (
                id, adjustment_id, description, quantity, price, tax_rate_id, tax_amount, total, chart_account_id, line_order
             ) VALUES (
                ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
             )'
        );

        foreach ($lines as $index => $line) {
            $id = $line['id'] ?? Database::uuid();
            $st->execute([
                $id,
                $adjustmentId,
                $line['description'] ?: null,
                $line['quantity'],
                $line['price'],
                $line['tax_rate_id'] ?: null,
                $line['tax_amount'],
                $line['total'],
                $line['chart_account_id'],
                $index,
            ]);
        }
    }

    public function deleteLines(string $adjustmentId): void
    {
        $st = $this->pdo->prepare('DELETE FROM sales_adjustment_lines WHERE adjustment_id = ?');
        $st->execute([$adjustmentId]);
    }

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

    public function nextDocumentNumber(string $companyId, string $type): string
    {
        $st = $this->pdo->prepare('SELECT COUNT(*) FROM sales_adjustments WHERE company_id = ? AND adjustment_type = ?');
        $st->execute([$companyId, $type]);
        $count = (int) $st->fetchColumn();

        $prefix = $type === 'credit_note' ? 'NC' : 'ND';
        return $prefix . '-' . str_pad((string) ($count + 1), 6, '0', STR_PAD_LEFT);
    }

    public function updateStatus(string $id, string $status, ?string $journalEntryId): void
    {
        $st = $this->pdo->prepare('UPDATE sales_adjustments SET status = ?, journal_entry_id = ? WHERE id = ?');
        $st->execute([$status, $journalEntryId, $id]);
    }
}
