<?php

namespace App\Services\Finance;

use App\Models\FinanceBalanceCarryforward;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class PriorYearBalanceCarryforwardService
{
    public function preview(array $filters): array
    {
        $from = $filters['from_school_year'];
        $to = $filters['to_school_year'];
        $minimum = (float) ($filters['minimum_balance'] ?? 0);
        $parentId = $filters['parent_id'] ?? null;

        $payments = $this->paymentTotals($from);
        $discounts = $this->discountTotals();
        $refunds = $this->refundTotals();

        $q = DB::table('invoices')->where('school_year', $from);
        if ($parentId) $q->where('parent_id', $parentId);
        $invoices = $q->get();

        $byParent = [];
        foreach ($invoices as $invoice) {
            $invoiceId = (int) $invoice->id;
            $balance = max(0, (float) ($invoice->total_amount ?? 0) - (float) ($payments[$invoiceId] ?? 0) - (float) ($discounts[$invoiceId] ?? 0) - (float) ($refunds[$invoiceId] ?? 0));
            if ($balance <= 0) continue;
            $pid = (int) $invoice->parent_id;
            if (!isset($byParent[$pid])) {
                $existing = FinanceBalanceCarryforward::query()->where('parent_id', $pid)->where('from_school_year', $from)->where('to_school_year', $to)->first();
                $byParent[$pid] = [
                    'parent_id' => $pid,
                    'from_school_year' => $from,
                    'to_school_year' => $to,
                    'source_invoice_ids' => [],
                    'source_balance_amount' => 0.0,
                    'proposed_carryforward_amount' => 0.0,
                    'existing_carryforward_id' => $existing?->id,
                    'warnings' => [],
                ];
                if ($existing) $byParent[$pid]['warnings'][] = 'source invoice already carried forward';
            }
            $byParent[$pid]['source_invoice_ids'][] = $invoiceId;
            $byParent[$pid]['source_balance_amount'] += $balance;
            $byParent[$pid]['proposed_carryforward_amount'] += $balance;
        }

        $rows = array_values(array_filter($byParent, fn($r) => $r['source_balance_amount'] >= $minimum));
        usort($rows, fn($a, $b) => $b['source_balance_amount'] <=> $a['source_balance_amount']);

        return ['fromSchoolYear' => $from, 'toSchoolYear' => $to, 'generatedAt' => now()->toISOString(), 'rows' => $rows];
    }

    public function storeDrafts(array $filters, ?int $actorId): array
    {
        $preview = $this->preview($filters);
        $created = [];
        foreach ($preview['rows'] as $row) {
            if (!empty($row['existing_carryforward_id'])) continue;
            $created[] = FinanceBalanceCarryforward::query()->create([
                'parent_id' => $row['parent_id'],
                'from_school_year' => $row['from_school_year'],
                'to_school_year' => $row['to_school_year'],
                'source_invoice_ids_json' => $row['source_invoice_ids'],
                'source_balance_amount' => $row['source_balance_amount'],
                'carryforward_amount' => $row['proposed_carryforward_amount'],
                'remaining_amount' => $row['proposed_carryforward_amount'],
                'status' => 'draft',
                'reason' => $filters['reason'] ?? null,
                'created_by' => $actorId,
                'metadata_json' => ['warnings' => $row['warnings']],
            ])->toArray();
        }
        return ['created' => $created, 'skipped' => count($preview['rows']) - count($created)];
    }

    public function approve(int $id, ?int $actorId): array
    {
        $cf = FinanceBalanceCarryforward::query()->findOrFail($id);
        $cf->fill(['status' => 'approved', 'approved_by' => $actorId, 'approved_at' => now()])->save();
        return $cf->fresh()->toArray();
    }

    public function waive(int $id, float $amount, ?string $reason, ?int $actorId): array
    {
        $cf = FinanceBalanceCarryforward::query()->findOrFail($id);
        $waived = min((float) $cf->remaining_amount, $amount);
        $cf->waived_amount = (float) $cf->waived_amount + $waived;
        $cf->remaining_amount = max(0, (float) $cf->remaining_amount - $waived);
        $cf->status = $cf->remaining_amount <= 0 ? 'waived' : $cf->status;
        $cf->reason = $reason ?: $cf->reason;
        $cf->approved_by = $cf->approved_by ?: $actorId;
        $cf->save();
        return $cf->fresh()->toArray();
    }

    public function adjust(int $id, float $amount, ?string $reason): array
    {
        $cf = FinanceBalanceCarryforward::query()->findOrFail($id);
        $cf->adjusted_amount = $amount;
        $cf->remaining_amount = max(0, (float) $cf->carryforward_amount - (float) $cf->waived_amount + $amount);
        $cf->reason = $reason ?: $cf->reason;
        $cf->save();
        return $cf->fresh()->toArray();
    }

    public function postToNewYear(int $id, ?int $actorId): array
    {
        return DB::transaction(function () use ($id, $actorId) {
            $cf = FinanceBalanceCarryforward::query()->lockForUpdate()->findOrFail($id);
            if (!in_array($cf->status, ['approved','draft','pending_review'], true)) {
                return $cf->toArray() + ['warning' => 'Carryforward is not in a postable status.'];
            }

            $amount = max(0, (float) $cf->carryforward_amount - (float) $cf->waived_amount + (float) $cf->adjusted_amount);
            $invoiceId = null;
            if (Schema::hasTable('invoices')) {
                $invoiceId = DB::table('invoices')->insertGetId([
                    'parent_id' => $cf->parent_id,
                    'invoice_number' => 'CF-' . $cf->to_school_year . '-' . $cf->id,
                    'total_amount' => $amount,
                    'balance' => $amount,
                    'paid_amount' => 0,
                    'school_year' => $cf->to_school_year,
                    'issue_date' => now()->toDateString(),
                    'status' => $amount > 0 ? 'unpaid' : 'paid',
                    'description' => 'Previous year balance from ' . $cf->from_school_year,
                    'created_at' => now(),
                    'updated_at' => now(),
                    'updated_by' => $actorId,
                ]);
            }
            $cf->posted_invoice_id = $invoiceId;
            $cf->status = 'posted_to_new_year';
            $cf->remaining_amount = $amount;
            $cf->save();
            return $cf->fresh()->toArray();
        });
    }

    public function report(array $filters): array
    {
        $q = FinanceBalanceCarryforward::query();
        foreach (['from_school_year','to_school_year','parent_id','status'] as $field) {
            if (!empty($filters[$field])) $q->where($field, $filters[$field]);
        }
        return ['generatedAt' => now()->toISOString(), 'rows' => $q->orderByDesc('remaining_amount')->get()->toArray()];
    }

    public function csvRows(array $report): array
    {
        $rows = [['ID','Parent ID','From Year','To Year','Source Invoices','Source Balance','Carryforward','Waived','Adjusted','Remaining','Status','Posted Invoice']];
        foreach ($report['rows'] as $r) {
            $rows[] = [$r['id'],$r['parent_id'],$r['from_school_year'],$r['to_school_year'],json_encode($r['source_invoice_ids_json'] ?? []),$r['source_balance_amount'],$r['carryforward_amount'],$r['waived_amount'],$r['adjusted_amount'],$r['remaining_amount'],$r['status'],$r['posted_invoice_id'] ?? null];
        }
        return $rows;
    }

    private function paymentTotals(string $schoolYear): array
    {
        if (!Schema::hasTable('payments')) return [];
        return DB::table('payments')->where('school_year', $schoolYear)->whereNotIn('status', ['void','voided','failed','cancelled','canceled','reversed'])->select('invoice_id', DB::raw('COALESCE(SUM(paid_amount),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all();
    }
    private function discountTotals(): array
    {
        if (!Schema::hasTable('discount_usages')) return [];
        $col = Schema::hasColumn('discount_usages','discount_amount') ? 'discount_amount' : 'amount';
        return DB::table('discount_usages')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all();
    }
    private function refundTotals(): array
    {
        if (!Schema::hasTable('refunds')) return [];
        $col = Schema::hasColumn('refunds','refund_amount') ? 'refund_amount' : (Schema::hasColumn('refunds','amount') ? 'amount' : null);
        if (!$col) return [];
        return DB::table('refunds')->whereNotIn('status', ['void','voided','cancelled','canceled'])->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all();
    }
}
