<?php

namespace App\Http\Controllers;

use App\Models\ChartOfAccount;
use App\Models\JournalEntry;
use App\Models\JournalEntryLine;
use App\Services\AccountingService;
use App\Services\FinancialPeriodService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class AccountsController extends Controller
{
    public function __construct(private AccountingService $accounting, private FinancialPeriodService $periods) {}

    public function chart()
    {
        $accounts = ChartOfAccount::with('parent')->orderBy('code')->get();
        return view('accounts.chart', compact('accounts'));
    }

    public function storeAccount(Request $request)
    {
        $data = $request->validate(['code' => 'required|string|max:30|unique:chart_of_accounts,code', 'name_en' => 'required|string|max:150', 'name_ar' => 'nullable|string|max:150', 'type' => 'required|in:asset,liability,equity,income,cogs,expense', 'parent_id' => 'nullable|exists:chart_of_accounts,id', 'allow_manual_posting' => 'nullable|boolean']);
        ChartOfAccount::create($data + ['status' => 'active']);
        return back()->with('success', 'Account created successfully.');
    }

    public function journals(Request $request)
    {
        $journals = JournalEntry::with('createdBy')->when($request->q, fn ($q) => $q->where('journal_no', 'like', '%'.$request->q.'%')->orWhere('reference_number', 'like', '%'.$request->q.'%'))->latest('entry_date')->paginate(30)->withQueryString();
        return view('accounts.journals', compact('journals'));
    }

    public function createJournal()
    {
        $accounts = ChartOfAccount::where('status', 'active')->where('allow_manual_posting', true)->orderBy('code')->get();
        return view('accounts.journal_create', compact('accounts'));
    }

    public function storeJournal(Request $request)
    {
        $data = $request->validate(['entry_date' => 'required|date', 'description' => 'required|string|max:1000', 'lines' => 'required|array|min:2', 'lines.*.account_id' => 'required|exists:chart_of_accounts,id', 'lines.*.description' => 'nullable|string|max:500', 'lines.*.debit' => 'nullable|numeric|min:0', 'lines.*.credit' => 'nullable|numeric|min:0']);
        try {
            $year = $this->periods->forDate($data['entry_date']);
            $entry = DB::transaction(fn () => $this->accounting->postJournal(['entry_date' => $data['entry_date'], 'reference_type' => 'manual', 'description' => $data['description'], 'financial_year_id' => $year->id], $data['lines']));
            return redirect()->route('accounts.journals')->with('success', 'Journal posted: '.$entry->journal_no);
        } catch (\Throwable $e) { report($e); return back()->withInput()->with('error', $e->getMessage()); }
    }

    public function ledger(Request $request)
    {
        $accounts = ChartOfAccount::where('status', 'active')->orderBy('code')->get();
        $selected = $request->account_id ? ChartOfAccount::findOrFail($request->account_id) : $accounts->first();
        $lines = JournalEntryLine::with(['journalEntry', 'account'])->when($selected, fn ($q) => $q->where('account_id', $selected->id))->when($request->date_from, fn ($q) => $q->whereHas('journalEntry', fn ($j) => $j->whereDate('entry_date', '>=', $request->date_from)))->when($request->date_to, fn ($q) => $q->whereHas('journalEntry', fn ($j) => $j->whereDate('entry_date', '<=', $request->date_to)))->whereHas('journalEntry', fn ($q) => $q->where('status', 'posted'))->get()->sortBy(fn ($line) => $line->journalEntry->entry_date->format('Y-m-d').str_pad((string) $line->id, 12, '0', STR_PAD_LEFT));
        return view('accounts.ledger', compact('accounts', 'selected', 'lines'));
    }

    public function trialBalance(Request $request)
    {
        $balances = ChartOfAccount::query()->leftJoin('journal_entry_lines', 'chart_of_accounts.id', '=', 'journal_entry_lines.account_id')->leftJoin('journal_entries', function ($join) use ($request) { $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id')->where('journal_entries.status', 'posted'); if ($request->date_to) $join->whereDate('journal_entries.entry_date', '<=', $request->date_to); })->groupBy('chart_of_accounts.id', 'chart_of_accounts.code', 'chart_of_accounts.name_en', 'chart_of_accounts.type')->select('chart_of_accounts.id', 'chart_of_accounts.code', 'chart_of_accounts.name_en', 'chart_of_accounts.type', DB::raw('COALESCE(SUM(journal_entry_lines.debit),0) as debit'), DB::raw('COALESCE(SUM(journal_entry_lines.credit),0) as credit'))->orderBy('code')->get();
        return view('accounts.trial_balance', compact('balances'));
    }

    public function financialStatement(Request $request, string $statement)
    {
        abort_unless(in_array($statement, ['profit-loss', 'balance-sheet']), 404);
        $types = $statement === 'profit-loss' ? ['income', 'cogs', 'expense'] : ['asset', 'liability', 'equity'];
        $accounts = ChartOfAccount::whereIn('type', $types)->withSum(['lines as debit_total' => fn ($q) => $q->whereHas('journalEntry', fn ($j) => $j->where('status', 'posted')->when($request->date_to, fn ($x) => $x->whereDate('entry_date', '<=', $request->date_to)))], 'debit')->withSum(['lines as credit_total' => fn ($q) => $q->whereHas('journalEntry', fn ($j) => $j->where('status', 'posted')->when($request->date_to, fn ($x) => $x->whereDate('entry_date', '<=', $request->date_to)))], 'credit')->orderBy('code')->get();
        return view('accounts.statement', compact('accounts', 'statement'));
    }
}
