<?php

declare(strict_types=1);

namespace App\Repositories;

use PDO;

final class CheckRepository
{
    public function __construct(private PDO $pdo)
    {
    }

    public function search(string $q, string $status): array
    {
        $where = ['1=1'];
        $params = [];
        if ($q !== '') {
            $like = '%' . $q . '%';
            $where[] = '(c.check_no LIKE ? OR p.name LIKE ? OR c.bank_name LIKE ?)';
            array_push($params, $like, $like, $like);
        }
        if ($status !== '' && in_array($status, ['received','endorsed','returned','settled_cash','replaced','canceled'], true)) {
            $where[] = 'c.current_status=?';
            $params[] = $status;
        }

        $sql = "SELECT c.*, p.name drawer_name, ep.name endorsed_party_name
                FROM checks c
                JOIN parties p ON p.id = c.drawer_party_id
                LEFT JOIN parties ep ON ep.id = c.last_endorsed_party_id
                WHERE " . implode(' AND ', $where) . "
                ORDER BY c.id DESC";
        $st = $this->pdo->prepare($sql);
        $st->execute($params);
        return $st->fetchAll() ?: [];
    }

    public function findById(int $id): array|false
    {
        $st = $this->pdo->prepare("SELECT c.*, p.name drawer_name, COALESCE(p.code, CONCAT('P',LPAD(p.id,5,'0'))) drawer_code, sp.name supplier_name
                     FROM checks c
                     JOIN parties p ON p.id=c.drawer_party_id
                     LEFT JOIN parties sp ON sp.id=c.last_endorsed_party_id
                     WHERE c.id=? LIMIT 1");
        $st->execute([$id]);
        return $st->fetch();
    }

    public function findBasicWithDrawer(int $id): array|false
    {
        $st = $this->pdo->prepare("SELECT c.*, p.name drawer_name FROM checks c JOIN parties p ON p.id=c.drawer_party_id WHERE c.id=? LIMIT 1");
        $st->execute([$id]);
        return $st->fetch();
    }

    public function findForReturn(int $id): array|false
    {
        $st = $this->pdo->prepare("SELECT c.*, p.name drawer_name, sp.name supplier_name FROM checks c JOIN parties p ON p.id=c.drawer_party_id LEFT JOIN parties sp ON sp.id=c.last_endorsed_party_id WHERE c.id=? LIMIT 1");
        $st->execute([$id]);
        return $st->fetch();
    }

    public function movements(int $checkId): array
    {
        $st = $this->pdo->prepare("SELECT m.*, p.name party_name FROM check_movements m LEFT JOIN parties p ON p.id=m.party_id WHERE m.check_id=? ORDER BY m.id ASC");
        $st->execute([$checkId]);
        return $st->fetchAll() ?: [];
    }

    public function activeCashboxes(): array
    {
        $st = $this->pdo->query("SELECT id,name FROM cashboxes WHERE is_active=1 ORDER BY id ASC");
        return $st->fetchAll() ?: [];
    }

    public function insertReceived(array $form, int $userId): int
    {
        $st = $this->pdo->prepare("INSERT INTO checks (check_no, amount, drawer_party_id, bank_name, issue_date, due_date, receive_date, current_status, current_holder_type, current_holder_party_id, notes, user_id)
                                 VALUES (?,?,?,?,?,?,?,'received','company',NULL,?,?)");
        $st->execute([
            $form['check_no'],
            (float)$form['amount'],
            $form['drawer_party_id'],
            $form['bank_name'] !== '' ? $form['bank_name'] : null,
            $form['issue_date'] !== '' ? $form['issue_date'] : null,
            $form['due_date'] !== '' ? $form['due_date'] : null,
            $form['movement_dt'],
            $form['notes'] !== '' ? $form['notes'] : null,
            $userId,
        ]);
        return (int)$this->pdo->lastInsertId();
    }

    public function addMovement(int $checkId, string $type, string $dt, ?int $partyId, float $amount, ?string $notes, int $userId): void
    {
        $this->pdo->prepare("INSERT INTO check_movements (check_id, movement_type, movement_date, party_id, amount, notes, user_id) VALUES (?,?,?,?,?,?,?)")
            ->execute([$checkId, $type, $dt, $partyId, $amount, $notes, $userId]);
    }

    public function updateCheckStatus(int $id, string $status, string $holderType, mixed $holderPartyId): void
    {
        $this->pdo->prepare("UPDATE checks SET current_status=?, current_holder_type=?, current_holder_party_id=?, updated_at=CURRENT_TIMESTAMP WHERE id=?")
            ->execute([$status, $holderType, $holderPartyId, $id]);
    }

    public function endorse(int $id, int $supplierPartyId): void
    {
        $this->pdo->prepare("UPDATE checks SET current_status='endorsed', current_holder_type='supplier', current_holder_party_id=?, last_endorsed_party_id=?, updated_at=CURRENT_TIMESTAMP WHERE id=?")
            ->execute([$supplierPartyId, $supplierPartyId, $id]);
    }
}
