<?php
declare(strict_types=1);

namespace App\Modules\Accounting\Models;

use App\Core\Model;
use App\Core\App;

class JournalEntry extends Model
{
    protected static string $table = 'journal_entries';
    protected static array $fillable = [
        'entry_number', 'fiscal_year_id', 'entry_date', 'reference_type',
        'reference_id', 'reference_number', 'description_ar', 'description_en',
        'total_debit', 'total_credit', 'currency', 'status', 'posted_at',
        'posted_by', 'reversed_entry_id', 'reversal_of_id', 'is_auto_generated',
        'source_module', 'branch_id', 'cost_center_id', 'period_closed',
        'attachments', 'notes',
    ];
    protected static bool $timestamps = true;
    protected static bool $softDelete = true;

    public function lines(): array
    {
        return JournalEntryLine::query()
            ->where('journal_entry_id', '=', $this->id)
            ->orderBy('line_number', 'ASC')
            ->get();
    }

    public static function generateEntryNumber(): string
    {
        $db = App::getInstance()->db();
        $year = date('Y');
        $prefix = 'JE-' . $year . '-';
        $last = $db->selectOne(
            "SELECT entry_number FROM journal_entries WHERE entry_number LIKE ? ORDER BY id DESC LIMIT 1",
            [$prefix . '%']
        );
        if ($last) {
            $parts = explode('-', $last['entry_number']);
            $seq = (int) end($parts) + 1;
        } else {
            $seq = 1;
        }
        return $prefix . str_pad((string) $seq, 6, '0', STR_PAD_LEFT);
    }

    public function isPosted(): bool
    {
        return $this->status === 'posted';
    }

    public function isDraft(): bool
    {
        return $this->status === 'draft';
    }

    public function isReversed(): bool
    {
        return $this->status === 'reversed';
    }

    public static function findByReference(string $type, int $id): ?static
    {
        $row = static::query()
            ->where('reference_type', '=', $type)
            ->where('reference_id', '=', $id)
            ->where('is_archived', '=', 0)
            ->first();
        if ($row === null) {
            return null;
        }
        $instance = new static($row);
        $instance->exists = true;
        return $instance;
    }

    public static function search(array $filters = [], int $page = 1, int $perPage = 25): array
    {
        $db = App::getInstance()->db();
        $where = ['je.is_archived = 0'];
        $params = [];

        if (!empty($filters['status'])) {
            $where[] = 'je.status = ?';
            $params[] = $filters['status'];
        }
        if (!empty($filters['date_from'])) {
            $where[] = 'je.entry_date >= ?';
            $params[] = $filters['date_from'];
        }
        if (!empty($filters['date_to'])) {
            $where[] = 'je.entry_date <= ?';
            $params[] = $filters['date_to'];
        }
        if (!empty($filters['source_module'])) {
            $where[] = 'je.source_module = ?';
            $params[] = $filters['source_module'];
        }
        if (!empty($filters['reference_type'])) {
            $where[] = 'je.reference_type = ?';
            $params[] = $filters['reference_type'];
        }
        if (!empty($filters['fiscal_year_id'])) {
            $where[] = 'je.fiscal_year_id = ?';
            $params[] = $filters['fiscal_year_id'];
        }
        if (!empty($filters['search'])) {
            $where[] = '(je.entry_number LIKE ? OR je.description_ar LIKE ? OR je.description_en LIKE ? OR je.reference_number LIKE ?'
                     . ' OR EXISTS (SELECT 1 FROM journal_entry_lines jl JOIN chart_of_accounts ca ON ca.id = jl.account_id WHERE jl.journal_entry_id = je.id AND (ca.account_code LIKE ? OR ca.name_ar LIKE ?)))';
            $term = '%' . $filters['search'] . '%';
            $params = array_merge($params, [$term, $term, $term, $term, $term, $term]);
        }

        $whereClause = implode(' AND ', $where);
        $offset = ($page - 1) * $perPage;

        $countRow = $db->selectOne("SELECT COUNT(*) as total FROM journal_entries je WHERE {$whereClause}", $params);
        $total = (int) ($countRow['total'] ?? 0);

        $rows = $db->select(
            "SELECT je.*, fy.name_ar as fiscal_year_name
             FROM journal_entries je
             LEFT JOIN fiscal_years fy ON fy.id = je.fiscal_year_id
             WHERE {$whereClause}
             ORDER BY je.entry_date DESC, je.id DESC
             LIMIT {$perPage} OFFSET {$offset}",
            $params
        );

        return [
            'data'     => $rows,
            'total'    => $total,
            'page'     => $page,
            'per_page' => $perPage,
            'pages'    => (int) ceil($total / $perPage),
        ];
    }
}
