<?php

namespace App\Http\Controllers\Admin\Finance\Accounting;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Account;
use App\Models\JournalEntry;
use App\Models\JournalEntryLine;

class AccountingController extends Controller
{
    public function __construct()
    {
        $this->middleware('can:finance.view')->only(['chartOfAccounts', 'journalEntries', 'ledger', 'trialBalance', 'balanceSheet', 'profitLoss']);
        $this->middleware('can:finance.add')->only(['storeAccount', 'storeJournalEntry']);
        $this->middleware('can:finance.edit')->only(['editAccount', 'updateAccount']);
        $this->middleware('can:finance.delete')->only(['destroyAccount']);
    }
    public function chartOfAccounts()
    {
        $query = Account::query();

        if (request('q')) {
            $q = trim(request('q'));
            $query->where(function($qrb) use ($q) {
                $qrb->where('account_name', 'like', "%$q%")
                    ->orWhere('account_code', 'like', "%$q%")
                    ->orWhere('description', 'like', "%$q%");
            });
        }

        if (request('type')) {
            $query->where('account_type', request('type'));
        }

        if (request('category')) {
            $query->where('account_category', request('category'));
        }

        if (request()->filled('status')) {
            $query->where('is_active', request('status') === 'active');
        }

        $accounts = $query->latest()->paginate(15)->withQueryString();
        return view('admin.finance.accounting.chart-of-accounts', compact('accounts'));
    }

    public function storeAccount(Request $request)
    {
        $validated = $request->validate([
            'account_code' => 'required|string|max:50|unique:accounts,account_code',
            'account_name' => 'required|string|max:255',
            'account_type' => 'required|in:asset,liability,equity,revenue,expense',
            'account_category' => 'required|in:current_asset,fixed_asset,current_liability,long_term_liability,equity,revenue,cost_of_goods_sold,operating_expense,other',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
        ]);

        $data = [
            'account_code' => $validated['account_code'],
            'account_name' => $validated['account_name'],
            'account_type' => $validated['account_type'],
            'account_category' => $validated['account_category'],
            'description' => $validated['description'] ?? null,
            'is_active' => $request->boolean('is_active', true),
        ];

        Account::create($data);

        return redirect()->route('finance.accounting.chart-of-accounts')->with('success', 'Account created successfully.');
    }

    public function editAccount(Account $account)
    {
        return view('admin.finance.accounting.edit-account', compact('account'));
    }

    public function updateAccount(Request $request, Account $account)
    {
        $validated = $request->validate([
            'account_code' => 'required|string|max:50|unique:accounts,account_code,' . $account->id,
            'account_name' => 'required|string|max:255',
            'account_type' => 'required|in:asset,liability,equity,revenue,expense',
            'account_category' => 'required|in:current_asset,fixed_asset,current_liability,long_term_liability,equity,revenue,cost_of_goods_sold,operating_expense,other',
            'description' => 'nullable|string',
            'is_active' => 'nullable|boolean',
        ]);

        $data = [
            'account_code' => $validated['account_code'],
            'account_name' => $validated['account_name'],
            'account_type' => $validated['account_type'],
            'account_category' => $validated['account_category'],
            'description' => $validated['description'] ?? null,
            'is_active' => $request->boolean('is_active', true),
        ];

        $account->update($data);

        return redirect()->route('finance.accounting.chart-of-accounts')->with('success', 'Account updated successfully.');
    }

    public function destroyAccount(Account $account)
    {
        $account->delete();

        return redirect()->route('finance.accounting.chart-of-accounts')->with('success', 'Account deleted successfully.');
    }

    public function journalEntries()
    {
        $query = JournalEntry::with(['lines.account', 'postedBy']);

        // Apply date filters if provided
        if (request('start_date')) {
            $query->whereDate('entry_date', '>=', request('start_date'));
        }

        if (request('end_date')) {
            $query->whereDate('entry_date', '<=', request('end_date'));
        }

        $journalEntries = $query->latest()->paginate(15);
        $accounts = Account::orderBy('account_name')->get();

        return view('admin.finance.accounting.journal-entries', compact('journalEntries', 'accounts'));
    }

    public function ledger(Request $request)
    {
        $accounts = Account::orderBy('account_code')->get();
        $selectedAccountId = $request->input('account_id');
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->endOfMonth()->toDateString());

        $transactions = collect();
        $openingBalance = 0;
        $selectedAccount = null;

        if ($selectedAccountId) {
            $selectedAccount = Account::findOrFail($selectedAccountId);

            // Calculate Opening Balance
            $openingBalance = JournalEntryLine::where('account_id', $selectedAccountId)
                ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
                ->where('journal_entries.entry_date', '<', $startDate)
                ->get()
                ->sum(function ($line) {
                    return $line->type === 'debit' ? $line->amount : -$line->amount;
                });

            // Fetch Transactions for the period
            $transactions = JournalEntryLine::with('journalEntry')
                ->where('account_id', $selectedAccountId)
                ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
                ->whereBetween('journal_entries.entry_date', [$startDate, $endDate])
                ->orderBy('journal_entries.entry_date')
                ->select('journal_entry_lines.*') // Avoid ambiguity
                ->get();
        }

        return view('admin.finance.accounting.ledger', compact(
            'accounts',
            'selectedAccount',
            'transactions',
            'openingBalance',
            'startDate',
            'endDate'
        ));
    }

    public function trialBalance()
    {
        $accounts = Account::with(['journalLines'])
            ->where('is_active', true)
            ->orderBy('account_code')
            ->get();

        $balances = $accounts->map(function ($account) {
            $debits = $account->journalLines->where('type', 'debit')->sum('amount');
            $credits = $account->journalLines->where('type', 'credit')->sum('amount');
            $balance = $debits - $credits;

            return [
                'account_code' => $account->account_code,
                'account_name' => $account->account_name,
                'debit' => $balance > 0 ? $balance : 0,
                'credit' => $balance < 0 ? abs($balance) : 0,
            ];
        });

        $totalDebit = $balances->sum('debit');
        $totalCredit = $balances->sum('credit');

        return view('admin.finance.accounting.trial-balance', compact('balances', 'totalDebit', 'totalCredit'));
    }

    public function balanceSheet()
    {
        $assets = Account::where('account_type', 'asset')->orderBy('account_category')->get()->groupBy('account_category');
        $liabilities = Account::where('account_type', 'liability')->orderBy('account_category')->get()->groupBy('account_category');
        $equity = Account::where('account_type', 'equity')->orderBy('account_category')->get()->groupBy('account_category');

        $totalAssets = $assets->flatten()->sum('balance');
        $totalLiabilities = $liabilities->flatten()->sum('balance');
        $totalEquity = $equity->flatten()->sum('balance');

        return view('admin.finance.accounting.balance-sheet', compact(
            'assets',
            'liabilities',
            'equity',
            'totalAssets',
            'totalLiabilities',
            'totalEquity'
        ));
    }

    public function profitLoss(Request $request)
    {
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->endOfMonth()->toDateString());

        $revenueAccounts = Account::where('account_type', 'revenue')->pluck('id');
        $expenseAccounts = Account::where('account_type', 'expense')->pluck('id');

        $totalRevenue = JournalEntryLine::whereIn('account_id', $revenueAccounts)
            ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
            ->whereBetween('journal_entries.entry_date', [$startDate, $endDate])
            ->sum('amount'); // Assuming revenue is credited

        $totalExpenses = JournalEntryLine::whereIn('account_id', $expenseAccounts)
            ->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
            ->whereBetween('journal_entries.entry_date', [$startDate, $endDate])
            ->sum('amount'); // Assuming expenses are debited

        $netProfit = $totalRevenue - $totalExpenses;

        $revenues = Account::with(['journalLines' => function ($query) use ($startDate, $endDate) {
            $query->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
                  ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]);
        }])->where('account_type', 'revenue')->get();

        $expenses = Account::with(['journalLines' => function ($query) use ($startDate, $endDate) {
            $query->join('journal_entries', 'journal_entry_lines.journal_entry_id', '=', 'journal_entries.id')
                  ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]);
        }])->where('account_type', 'expense')->get();

        return view('admin.finance.accounting.profit-loss', compact(
            'revenues',
            'expenses',
            'totalRevenue',
            'totalExpenses',
            'netProfit',
            'startDate',
            'endDate'
        ));
    }

    /**
     * Export filtered accounts as CSV
     */
    public function exportAccounts(Request $request)
    {
        $query = Account::query();

        if ($request->filled('q')) {
            $q = trim($request->q);
            $query->where(function($qrb) use ($q) {
                $qrb->where('account_name', 'like', "%$q%")
                    ->orWhere('account_code', 'like', "%$q%")
                    ->orWhere('description', 'like', "%$q%");
            });
        }
        if ($request->filled('type')) {
            $query->where('account_type', $request->type);
        }
        if ($request->filled('category')) {
            $query->where('account_category', $request->category);
        }
        if ($request->filled('status')) {
            $query->where('is_active', $request->status === 'active');
        }

        $rows = $query->orderBy('account_code')->get(['account_code','account_name','account_type','account_category','is_active','description']);

        $headers = [
            'Content-Type' => 'text/csv',
            'Content-Disposition' => 'attachment; filename="accounts-export-'.date('Ymd_His').'.csv"',
        ];

        $callback = function() use ($rows) {
            $out = fopen('php://output', 'w');
            fputcsv($out, ['Code','Name','Type','Category','Status','Description']);
            foreach ($rows as $r) {
                fputcsv($out, [
                    $r->account_code,
                    $r->account_name,
                    ucfirst($r->account_type),
                    ucwords(str_replace('_',' ', $r->account_category)),
                    $r->is_active ? 'Active' : 'Inactive',
                    $r->description,
                ]);
            }
            fclose($out);
        };

        return response()->stream($callback, 200, $headers);
    }

    /**
     * Store a newly created journal entry with lines.
     */
    public function storeJournalEntry(Request $request)
    {
        $validated = $request->validate([
            'entry_date' => 'required|date',
            'description' => 'required|string|max:500',
            'lines' => 'required|array|min:2',
            'lines.*.account_id' => 'required|exists:accounts,id',
            'lines.*.type' => 'required|in:debit,credit',
            'lines.*.amount' => 'required|numeric|min:0.01',
        ]);

        $totalDebit = 0; $totalCredit = 0;
        foreach ($validated['lines'] as $line) {
            if ($line['type'] === 'debit') {
                $totalDebit += (float) $line['amount'];
            } else {
                $totalCredit += (float) $line['amount'];
            }
        }

        if (round($totalDebit, 2) !== round($totalCredit, 2)) {
            return back()->with('error', 'Debits and credits must be equal.')->withInput();
        }

        $entry = JournalEntry::create([
            'entry_number' => 'JE-' . date('Ymd') . '-' . str_pad((string)random_int(1, 9999), 4, '0', STR_PAD_LEFT),
            'entry_date' => $validated['entry_date'],
            'description' => $validated['description'],
            'total_debit' => $totalDebit,
            'total_credit' => $totalCredit,
            'status' => 'posted',
            'posted_by' => auth()->id(),
            'posted_at' => now(),
        ]);

        foreach ($validated['lines'] as $line) {
            JournalEntryLine::create([
                'journal_entry_id' => $entry->id,
                'account_id' => $line['account_id'],
                'type' => $line['type'],
                'amount' => $line['amount'],
            ]);
        }

        return redirect()->route('admin.finance.accounting.journal-entries')->with('success', 'Journal entry posted successfully.');
    }
}
