<?php

namespace App\Http\Controllers;

use App\Models\Expense;
use App\Models\ExpenseArchive;
use App\Models\ExpenseArchiveItem;
use App\Models\MonthlySettlement;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class AccountsExpensesController extends Controller
{
    private const TYPES = [
        'hospitality',
        'cleaning_materials',
        'fixed_expenses',
        'general_purchases',
        'servers',
        'ads',
        'online_purchases',
        'other',
    ];

    public function index()
    {
        $this->backfillLatestSettlementExpensesIfNeeded();

        $tz = 'Asia/Baghdad';
        $currentStart = Carbon::now($tz)->startOfMonth()->toDateString();

        $lastSettlement = MonthlySettlement::query()->orderByDesc('settled_at')->orderByDesc('id')->first();
        if ($lastSettlement?->date_to) {
            $afterLastSettlement = Carbon::parse($lastSettlement->date_to, $tz)->addDay()->toDateString();
            if ($afterLastSettlement > $currentStart) {
                $currentStart = $afterLastSettlement;
            }
        }

        $expenses = Expense::query()
            ->whereDate('expense_date', '>=', $currentStart)
            ->orderByDesc('expense_date')
            ->orderByDesc('id')
            ->paginate(20);

        $paymentMethods = [
            'Cash',
            'Zain Cash',
            'POS',
            'Qi',
            'Switch',
        ];

        return view('accounts.expenses', compact('expenses', 'paymentMethods'));
    }

    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), $this->rules());

        if ($validator->fails()) {
            return redirect()
                ->route('accounts.expenses')
                ->withErrors($validator)
                ->withInput()
                ->with('open_expense_add_modal', true);
        }

        $data = $validator->validated();
        $data['total'] = (int) $data['price'] * (int) $data['quantity'];
        $data['created_by'] = Auth::id();

        Expense::query()->create($data);

        return redirect()->route('accounts.expenses')->with('success', __('accounts.expenses.messages.created'));
    }

    public function update(Request $request, int $id)
    {
        $expense = Expense::query()->findOrFail($id);

        $validator = Validator::make($request->all(), $this->rules());

        if ($validator->fails()) {
            return redirect()
                ->route('accounts.expenses')
                ->withErrors($validator)
                ->withInput()
                ->with('edit_expense_id', $expense->id);
        }

        $data = $validator->validated();
        $data['total'] = (int) $data['price'] * (int) $data['quantity'];

        $expense->update($data);

        return redirect()->route('accounts.expenses')->with('success', __('accounts.expenses.messages.updated'));
    }

    public function destroy(int $id)
    {
        $expense = Expense::query()->findOrFail($id);
        $expense->delete();

        return redirect()->route('accounts.expenses')->with('success', __('accounts.expenses.messages.deleted'));
    }

    public function archives()
    {
        $archives = ExpenseArchive::query()
            ->with('settler')
            ->orderByDesc('settlement_date')
            ->orderByDesc('id')
            ->paginate(12);

        return view('accounts.expenses_archives.index', [
            'archives' => $archives,
        ]);
    }

    public function archiveShow(ExpenseArchive $archive)
    {
        $archive->load(['settler', 'items.creator']);

        $items = ExpenseArchiveItem::query()
            ->where('archive_id', $archive->id)
            ->with('creator')
            ->orderByDesc('expense_date')
            ->orderByDesc('id')
            ->paginate(50);

        return view('accounts.expenses_archives.show', [
            'archive' => $archive,
            'items' => $items,
        ]);
    }

    public function archivePrint(ExpenseArchive $archive)
    {
        $archive->load(['settler', 'items.creator']);

        $items = ExpenseArchiveItem::query()
            ->where('archive_id', $archive->id)
            ->with('creator')
            ->orderBy('expense_date')
            ->orderBy('id')
            ->get();

        return view('accounts.expenses_archives.print', [
            'archive' => $archive,
            'items' => $items,
        ]);
    }

    private function backfillLatestSettlementExpensesIfNeeded(): void
    {
        $latest = MonthlySettlement::query()
            ->orderByDesc('settled_at')
            ->orderByDesc('id')
            ->first();

        if (! $latest || ! $latest->date_from || ! $latest->date_to) {
            return;
        }

        $tz = 'Asia/Baghdad';
        $dateFrom = Carbon::parse($latest->date_from, $tz);
        $dateTo = Carbon::parse($latest->date_to, $tz);
        $monthKey = $dateFrom->format('Y-m');

        $expenses = Expense::query()
            ->whereBetween('expense_date', [$dateFrom->toDateString(), $dateTo->toDateString()])
            ->orderByDesc('expense_date')
            ->orderByDesc('id')
            ->get();

        if ($expenses->isEmpty()) {
            return;
        }

        DB::transaction(function () use ($latest, $dateFrom, $monthKey, $expenses) {
            $titleEn = $dateFrom->copy()->locale('en')->translatedFormat('F Y');
            $titleAr = $dateFrom->copy()->locale('ar')->translatedFormat('F Y');
            $settlementDate = $latest->settled_at ? Carbon::parse($latest->settled_at)->toDateString() : now()->toDateString();

            $archive = ExpenseArchive::query()->firstOrCreate(
                ['month_key' => $monthKey],
                [
                    'title_ar' => $titleAr,
                    'title_en' => $titleEn,
                    'settlement_date' => $settlementDate,
                    'settled_by' => $latest->created_by,
                    'total_amount' => 0,
                    'items_count' => 0,
                ]
            );

            $existingExpenseIds = ExpenseArchiveItem::query()
                ->where('archive_id', $archive->id)
                ->whereNotNull('expense_id')
                ->pluck('expense_id')
                ->map(fn ($v) => (int) $v)
                ->all();
            $existingExpenseIds = array_flip($existingExpenseIds);

            foreach ($expenses as $expense) {
                $expenseId = (int) $expense->id;
                if (isset($existingExpenseIds[$expenseId])) {
                    continue;
                }
                ExpenseArchiveItem::query()->create([
                    'archive_id' => $archive->id,
                    'expense_id' => $expense->id,
                    'name' => $expense->name,
                    'details' => $expense->details,
                    'price' => (int) $expense->price,
                    'quantity' => (int) $expense->quantity,
                    'total' => (int) $expense->total,
                    'payment_method' => $expense->payment_method,
                    'expense_type' => $expense->expense_type,
                    'custom_type' => $expense->custom_type,
                    'expense_date' => $expense->expense_date,
                    'created_by' => $expense->created_by,
                ]);
            }

            $archiveTotals = ExpenseArchiveItem::query()
                ->where('archive_id', $archive->id)
                ->selectRaw('COALESCE(SUM(total), 0) as total_amount')
                ->selectRaw('COUNT(*) as items_count')
                ->first();

            $archive->forceFill([
                'total_amount' => (int) ($archiveTotals?->total_amount ?? 0),
                'items_count' => (int) ($archiveTotals?->items_count ?? 0),
            ])->save();

            Expense::query()->whereIn('id', $expenses->pluck('id')->all())->delete();
        });
    }

    private function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'details' => ['nullable', 'string'],
            'price' => ['required', 'integer', 'min:0'],
            'quantity' => ['required', 'integer', 'min:1'],
            'total' => ['required', 'integer', 'min:0'],
            'payment_method' => ['required', 'string', 'in:Cash,Zain Cash,POS,Qi,Switch'],
            'expense_type' => ['required', 'string', 'in:'.implode(',', self::TYPES)],
            'custom_type' => ['nullable', 'string', 'max:255', 'required_if:expense_type,other'],
            'expense_date' => ['required', 'date'],
        ];
    }
}
