<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\FinancialTransaction;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\StreamedResponse;

class AccountStatementController extends Controller
{
    public function __construct()
    {
        $this->middleware('can:accounts.view')->only(['index', 'export']);
        $this->middleware('can:accounts.manage')->only(['store', 'destroy']);
    }

    public function index(Request $request)
    {
        $filters = $this->filters($request);
        $manual = $this->manualQuery($filters)->with('creator')->get()->map(fn ($row) => ['id' => $row->id, 'source' => 'manual', 'date' => $row->transaction_date, 'type' => $row->type, 'category' => $row->category, 'description' => $row->description, 'method' => $row->payment_method, 'reference' => $row->reference, 'income' => $row->type === 'income' ? (float) $row->amount : 0, 'expense' => $row->type === 'expense' ? (float) $row->amount : 0, 'by' => $row->creator?->name]);
        $payments = $this->paymentQuery($filters)->with(['customer', 'entryBy'])->get()->map(fn ($row) => ['id' => $row->id, 'source' => 'billing', 'date' => $row->entry_date, 'type' => 'income', 'category' => (int) $row->type === 99 ? 'Discount' : 'Customer Payment', 'description' => $row->description ?: 'Payment from '.$row->customer?->name, 'method' => 'billing', 'reference' => $row->customer?->customer_code, 'income' => (int) $row->type === 99 ? 0 : (float) $row->amount, 'expense' => 0, 'by' => $row->entryBy?->name]);
        $rows = $manual->concat($payments)->sortBy(fn ($row) => ($row['date']?->format('Y-m-d') ?? '').$row['source'].$row['id'])->values();
        $opening = $this->openingBalance($filters['from'] ?? null);
        $income = $rows->sum('income');
        $expense = $rows->sum('expense');

        return view('admin.accounts.statement', compact('rows', 'opening', 'income', 'expense', 'filters'));
    }

    public function store(Request $request)
    {
        $data = $request->validate(['type' => ['required', Rule::in(['income', 'expense'])], 'category' => ['required', 'max:100'], 'amount' => ['required', 'numeric', 'gt:0'], 'payment_method' => ['required', Rule::in(['cash', 'bank', 'bkash', 'nagad', 'other'])], 'reference' => ['nullable', 'max:255'], 'description' => ['nullable', 'string'], 'transaction_date' => ['required', 'date']]);
        FinancialTransaction::create($data + ['created_by' => $request->user()->id]);

        return back()->with('success', 'Account transaction saved.');
    }

    public function destroy(FinancialTransaction $transaction)
    {
        $transaction->delete();

        return back()->with('success', 'Account transaction deleted.');
    }

    public function export(Request $request): StreamedResponse
    {
        $response = $this->index($request)->getData();

        return response()->streamDownload(function () use ($response) {
            $out = fopen('php://output', 'w');
            fputcsv($out, ['Date', 'Source', 'Category', 'Description', 'Method', 'Reference', 'Income', 'Expense']);
            foreach ($response['rows'] as $row) {
                fputcsv($out, [$row['date']?->format('Y-m-d'), $row['source'], $row['category'], $row['description'], $row['method'], $row['reference'], $row['income'], $row['expense']]);
            }
            fclose($out);
        }, 'account-statement-'.now()->format('Ymd-His').'.csv', ['Content-Type' => 'text/csv']);
    }

    private function filters(Request $request): array
    {
        return $request->validate(['from' => ['nullable', 'date'], 'to' => ['nullable', 'date', 'after_or_equal:from'], 'type' => ['nullable', Rule::in(['income', 'expense'])], 'method' => ['nullable', 'max:40']]);
    }

    private function manualQuery(array $f)
    {
        return FinancialTransaction::query()->when($f['from'] ?? null, fn ($q, $v) => $q->whereDate('transaction_date', '>=', $v))->when($f['to'] ?? null, fn ($q, $v) => $q->whereDate('transaction_date', '<=', $v))->when($f['type'] ?? null, fn ($q, $v) => $q->where('type', $v))->when($f['method'] ?? null, fn ($q, $v) => $q->where('payment_method', $v));
    }

    private function paymentQuery(array $f)
    {
        return Account::query()->whereIn('type', [1, 99])->when(($f['type'] ?? null) === 'expense', fn ($q) => $q->whereRaw('1=0'))->when($f['from'] ?? null, fn ($q, $v) => $q->whereDate('entry_date', '>=', $v))->when($f['to'] ?? null, fn ($q, $v) => $q->whereDate('entry_date', '<=', $v))->when($f['method'] ?? null, fn ($q) => $q->whereRaw('1=0'));
    }

    private function openingBalance(?string $from): float
    {
        if (! $from) {
            return 0;
        } $manual = FinancialTransaction::whereDate('transaction_date', '<', $from)->selectRaw("SUM(CASE WHEN type='income' THEN amount ELSE -amount END) balance")->value('balance');
        $payments = Account::where('type', 1)->whereDate('entry_date', '<', $from)->sum('amount');

        return (float) $manual + (float) $payments;
    }
}
