<?php

namespace App\Core\Services\Accounting;

use App\Core\Repositories\ChartOfAccountRepository;
use App\Core\Repositories\ExpenseRepository;
use App\Core\Repositories\IncomeRepository;
use App\Core\Repositories\JournalEntryRepository;
use Illuminate\Support\Facades\DB;

/**
 * Financial Report Service
 * Generates financial reports (Trial Balance, P&L, Tax Reports)
 */
class FinancialReportService
{
    public function __construct(
        private ChartOfAccountRepository $accountRepository,
        private JournalEntryRepository $journalRepository,
        private ExpenseRepository $expenseRepository,
        private IncomeRepository $incomeRepository
    ) {}

    /**
     * Generate Trial Balance
     */
    public function generateTrialBalance(?string $dateFrom = null, ?string $dateTo = null): array
    {
        $dateTo = $dateTo ?? now()->format('Y-m-d');
        $dateFrom = $dateFrom ?? now()->startOfYear()->format('Y-m-d');

        $accounts = $this->accountRepository->getAll();
        $trialBalance = [];

        foreach ($accounts as $account) {
            $debit = 0;
            $credit = 0;

            // Get journal entry lines for this account in date range
            $lines = DB::table('journal_entry_lines')
                ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
                ->where('journal_entry_lines.account_id', $account->id)
                ->where('journal_entries.status', 'posted')
                ->whereBetween('journal_entries.entry_date', [$dateFrom, $dateTo])
                ->select('journal_entry_lines.type', 'journal_entry_lines.amount')
                ->get();

            foreach ($lines as $line) {
                if ($line->type === 'debit') {
                    $debit += $line->amount;
                } else {
                    $credit += $line->amount;
                }
            }

            // Calculate balance
            $balance = 0;
            if ($account->isDebitAccount()) {
                $balance = $account->opening_balance + $debit - $credit;
            } else {
                $balance = $account->opening_balance + $credit - $debit;
            }

            if ($balance != 0 || $debit != 0 || $credit != 0) {
                $trialBalance[] = [
                    'account' => $account,
                    'debit' => $debit,
                    'credit' => $credit,
                    'balance' => $balance,
                ];
            }
        }

        return $trialBalance;
    }

    /**
     * Generate Profit & Loss Statement
     */
    public function generateProfitLoss(?string $dateFrom = null, ?string $dateTo = null): array
    {
        $dateTo = $dateTo ?? now()->format('Y-m-d');
        $dateFrom = $dateFrom ?? now()->startOfYear()->format('Y-m-d');

        // Revenue
        $revenueAccounts = $this->accountRepository->getByType('revenue');
        $totalRevenue = 0;
        $revenueDetails = [];

        foreach ($revenueAccounts as $account) {
            $amount = $this->getAccountBalance($account->id, $dateFrom, $dateTo, 'credit');
            if ($amount > 0) {
                $revenueDetails[] = [
                    'account' => $account,
                    'amount' => $amount,
                ];
                $totalRevenue += $amount;
            }
        }

        // Expenses
        $expenseAccounts = $this->accountRepository->getByType('expense');
        $totalExpenses = 0;
        $expenseDetails = [];

        foreach ($expenseAccounts as $account) {
            $amount = $this->getAccountBalance($account->id, $dateFrom, $dateTo, 'debit');
            if ($amount > 0) {
                $expenseDetails[] = [
                    'account' => $account,
                    'amount' => $amount,
                ];
                $totalExpenses += $amount;
            }
        }

        $netIncome = $totalRevenue - $totalExpenses;

        return [
            'date_from' => $dateFrom,
            'date_to' => $dateTo,
            'revenue' => [
                'details' => $revenueDetails,
                'total' => $totalRevenue,
            ],
            'expenses' => [
                'details' => $expenseDetails,
                'total' => $totalExpenses,
            ],
            'net_income' => $netIncome,
        ];
    }

    /**
     * Generate Tax Report
     */
    public function generateTaxReport(?string $dateFrom = null, ?string $dateTo = null): array
    {
        $dateTo = $dateTo ?? now()->format('Y-m-d');
        $dateFrom = $dateFrom ?? now()->startOfYear()->format('Y-m-d');

        // Get all income with tax
        $income = \App\Models\Income::where('income_date', '>=', $dateFrom)
            ->where('income_date', '<=', $dateTo)
            ->where('status', '!=', 'reversed')
            ->get();

        $totalIncome = $income->sum('total');
        $totalTaxCollected = $income->sum('tax_amount');

        // Get all expenses with tax
        $expenses = \App\Models\Expense::where('expense_date', '>=', $dateFrom)
            ->where('expense_date', '<=', $dateTo)
            ->where('status', '!=', 'rejected')
            ->get();

        $totalExpenses = $expenses->sum('total');
        $totalTaxPaid = $expenses->sum('tax_amount');

        $netTaxOwed = $totalTaxCollected - $totalTaxPaid;

        return [
            'date_from' => $dateFrom,
            'date_to' => $dateTo,
            'income' => [
                'total' => $totalIncome,
                'tax_collected' => $totalTaxCollected,
                'count' => $income->count(),
            ],
            'expenses' => [
                'total' => $totalExpenses,
                'tax_paid' => $totalTaxPaid,
                'count' => $expenses->count(),
            ],
            'net_tax_owed' => $netTaxOwed,
        ];
    }

    /**
     * Get account balance for date range
     */
    private function getAccountBalance(int $accountId, string $dateFrom, string $dateTo, string $type): float
    {
        $account = $this->accountRepository->getById($accountId);
        if (! $account) {
            return 0;
        }

        $result = DB::table('journal_entry_lines')
            ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
            ->where('journal_entry_lines.account_id', $accountId)
            ->where('journal_entries.status', 'posted')
            ->whereBetween('journal_entries.entry_date', [$dateFrom, $dateTo])
            ->where('journal_entry_lines.type', $type)
            ->sum('journal_entry_lines.amount');

        return (float) ($result ?? 0);
    }

    /**
     * Export report to CSV
     */
    public function exportToCsv(array $data, string $filename): string
    {
        $path = storage_path('app/reports/'.$filename);
        $directory = dirname($path);

        if (! is_dir($directory)) {
            mkdir($directory, 0755, true);
        }

        $file = fopen($path, 'w');

        // Write headers
        if (isset($data[0]) && is_array($data[0])) {
            fputcsv($file, array_keys($data[0]));
        }

        // Write data
        foreach ($data as $row) {
            if (is_array($row)) {
                fputcsv($file, array_values($row));
            }
        }

        fclose($file);

        return $path;
    }
}
