<?php
declare(strict_types=1);

namespace App\Modules\Accounting\Services;

use App\Core\App;

/**
 * Generates income statement (profit & loss) and balance sheet
 * from posted journal entries.
 */
final class FinancialReportService
{
    /**
     * Income Statement (قائمة الدخل)
     * Revenue - Expenses = Net Income
     */
    public static function getIncomeStatement(
        string  $dateFrom,
        string  $dateTo,
        ?int    $costCenterId = null,
        ?int    $branchId = null
    ): array {
        $db = App::getInstance()->db();

        $extraWhere = '';
        $params = [$dateFrom, $dateTo];

        if ($costCenterId !== null) {
            $extraWhere .= ' AND jel.cost_center_id = ?';
            $params[] = $costCenterId;
        }
        if ($branchId !== null) {
            $extraWhere .= ' AND jel.branch_id = ?';
            $params[] = $branchId;
        }

        // Revenue accounts (credit-nature)
        $revenue = $db->select(
            "SELECT coa.id, coa.account_code, coa.name_ar, coa.name_en,
                    COALESCE(SUM(jel.credit), 0) - COALESCE(SUM(jel.debit), 0) as balance
             FROM chart_of_accounts coa
             LEFT JOIN journal_entry_lines jel ON jel.account_id = coa.id
                 AND jel.journal_entry_id IN (
                     SELECT id FROM journal_entries
                     WHERE status = 'posted' AND entry_date >= ? AND entry_date <= ? AND is_archived = 0
                 )
                 {$extraWhere}
             WHERE coa.account_type = 'revenue' AND coa.is_archived = 0 AND coa.is_header = 0
             GROUP BY coa.id, coa.account_code, coa.name_ar, coa.name_en
             HAVING balance != 0
             ORDER BY coa.account_code ASC",
            $params
        );

        // Expense accounts (debit-nature)
        $expenses = $db->select(
            "SELECT coa.id, coa.account_code, coa.name_ar, coa.name_en,
                    COALESCE(SUM(jel.debit), 0) - COALESCE(SUM(jel.credit), 0) as balance
             FROM chart_of_accounts coa
             LEFT JOIN journal_entry_lines jel ON jel.account_id = coa.id
                 AND jel.journal_entry_id IN (
                     SELECT id FROM journal_entries
                     WHERE status = 'posted' AND entry_date >= ? AND entry_date <= ? AND is_archived = 0
                 )
                 {$extraWhere}
             WHERE coa.account_type = 'expense' AND coa.is_archived = 0 AND coa.is_header = 0
             GROUP BY coa.id, coa.account_code, coa.name_ar, coa.name_en
             HAVING balance != 0
             ORDER BY coa.account_code ASC",
            $params
        );

        $totalRevenue = '0.00';
        foreach ($revenue as $r) {
            $totalRevenue = bcadd($totalRevenue, (string) $r['balance'], 2);
        }

        $totalExpenses = '0.00';
        foreach ($expenses as $e) {
            $totalExpenses = bcadd($totalExpenses, (string) $e['balance'], 2);
        }

        $netIncome = bcsub($totalRevenue, $totalExpenses, 2);

        return [
            'revenue'        => $revenue,
            'expenses'       => $expenses,
            'total_revenue'  => $totalRevenue,
            'total_expenses' => $totalExpenses,
            'net_income'     => $netIncome,
            'date_from'      => $dateFrom,
            'date_to'        => $dateTo,
        ];
    }

    /**
     * Balance Sheet (الميزانية العمومية)
     * Assets = Liabilities + Equity
     */
    public static function getBalanceSheet(
        string  $asOfDate,
        ?int    $costCenterId = null,
        ?int    $branchId = null
    ): array {
        $db = App::getInstance()->db();

        $extraWhere = '';
        $params = [$asOfDate];

        if ($costCenterId !== null) {
            $extraWhere .= ' AND jel.cost_center_id = ?';
            $params[] = $costCenterId;
        }
        if ($branchId !== null) {
            $extraWhere .= ' AND jel.branch_id = ?';
            $params[] = $branchId;
        }

        // Build balance for each account type up to asOfDate
        $allAccounts = $db->select(
            "SELECT coa.id, coa.account_code, coa.name_ar, coa.name_en,
                    coa.account_type, coa.account_nature, coa.opening_balance,
                    COALESCE(SUM(jel.debit), 0) as total_debit,
                    COALESCE(SUM(jel.credit), 0) as total_credit
             FROM chart_of_accounts coa
             LEFT JOIN journal_entry_lines jel ON jel.account_id = coa.id
                 AND jel.journal_entry_id IN (
                     SELECT id FROM journal_entries
                     WHERE status = 'posted' AND entry_date <= ? AND is_archived = 0
                 )
                 {$extraWhere}
             WHERE coa.is_archived = 0 AND coa.is_header = 0
               AND coa.account_type IN ('asset','liability','equity')
             GROUP BY coa.id, coa.account_code, coa.name_ar, coa.name_en,
                      coa.account_type, coa.account_nature, coa.opening_balance
             ORDER BY coa.account_code ASC",
            $params
        );

        $assets = [];
        $liabilities = [];
        $equity = [];
        $totalAssets = '0.00';
        $totalLiabilities = '0.00';
        $totalEquity = '0.00';

        foreach ($allAccounts as $acc) {
            $opening = (string) $acc['opening_balance'];
            if ($acc['account_nature'] === 'debit') {
                $balance = bcadd($opening, bcsub((string) $acc['total_debit'], (string) $acc['total_credit'], 2), 2);
            } else {
                $balance = bcadd($opening, bcsub((string) $acc['total_credit'], (string) $acc['total_debit'], 2), 2);
            }

            if (bccomp($balance, '0.00', 2) === 0) {
                continue;
            }

            $acc['balance'] = $balance;

            switch ($acc['account_type']) {
                case 'asset':
                    $assets[] = $acc;
                    $totalAssets = bcadd($totalAssets, $balance, 2);
                    break;
                case 'liability':
                    $liabilities[] = $acc;
                    $totalLiabilities = bcadd($totalLiabilities, $balance, 2);
                    break;
                case 'equity':
                    $equity[] = $acc;
                    $totalEquity = bcadd($totalEquity, $balance, 2);
                    break;
            }
        }

        // Calculate current-period net income and add to equity
        $fiscalYearStart = self::getFiscalYearStart($asOfDate);
        $incomeStatement = self::getIncomeStatement($fiscalYearStart, $asOfDate, $costCenterId, $branchId);
        $netIncome = $incomeStatement['net_income'];

        if (bccomp($netIncome, '0.00', 2) !== 0) {
            $equity[] = [
                'account_code' => '---',
                'name_ar'      => 'صافي ربح/خسارة الفترة',
                'name_en'      => 'Current Period Net Income',
                'balance'      => $netIncome,
            ];
            $totalEquity = bcadd($totalEquity, $netIncome, 2);
        }

        $totalLiabilitiesAndEquity = bcadd($totalLiabilities, $totalEquity, 2);

        return [
            'assets'                      => $assets,
            'liabilities'                 => $liabilities,
            'equity'                      => $equity,
            'total_assets'                => $totalAssets,
            'total_liabilities'           => $totalLiabilities,
            'total_equity'                => $totalEquity,
            'total_liabilities_and_equity' => $totalLiabilitiesAndEquity,
            'is_balanced'                 => bccomp($totalAssets, $totalLiabilitiesAndEquity, 2) === 0,
            'as_of_date'                  => $asOfDate,
        ];
    }

    /**
     * Multi-branch consolidation — aggregate all branches into one balance sheet.
     */
    public static function getConsolidatedBalanceSheet(string $asOfDate): array
    {
        $db = App::getInstance()->db();
        $branches = $db->select("SELECT id, name_ar, name_en FROM branches WHERE is_archived = 0 ORDER BY id");

        $branchSheets = [];
        foreach ($branches as $branch) {
            $branchSheets[] = [
                'branch'        => $branch,
                'balance_sheet' => self::getBalanceSheet($asOfDate, null, (int) $branch['id']),
            ];
        }

        // Consolidated (all branches)
        $consolidated = self::getBalanceSheet($asOfDate);

        return [
            'consolidated'  => $consolidated,
            'branch_sheets' => $branchSheets,
            'as_of_date'    => $asOfDate,
        ];
    }

    private static function getFiscalYearStart(string $date): string
    {
        $config = App::getInstance()->config('app');
        $startMonth = (int) ($config['financial_year_start_month'] ?? 7);
        $startDay = (int) ($config['financial_year_start_day'] ?? 1);

        $year = (int) date('Y', strtotime($date));
        $month = (int) date('m', strtotime($date));

        if ($month < $startMonth) {
            $year--;
        }

        return sprintf('%04d-%02d-%02d', $year, $startMonth, $startDay);
    }
}
