<?php

namespace App\Core\Services\Recurring;

use App\Core\Services\Invoice\InvoiceService;
use App\Models\RecurringInvoice;
use App\Services\ReminderService;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Recurring Invoice Service
 * Handles recurring invoice generation and management
 */
class RecurringInvoiceService
{
    public function __construct(
        private InvoiceService $invoiceService,
        private ReminderService $reminderService
    ) {}

    /**
     * Create a new recurring invoice
     */
    public function create(array $data): RecurringInvoice
    {
        // Calculate next invoice date
        $nextInvoiceDate = $this->calculateNextInvoiceDate(
            $data['start_date'],
            $data['frequency']
        );

        return RecurringInvoice::create([
            'name' => $data['name'],
            'customer_id' => $data['customer_id'],
            'frequency' => $data['frequency'],
            'start_date' => $data['start_date'],
            'end_date' => $data['end_date'] ?? null,
            'occurrences' => $data['occurrences'] ?? null,
            'next_invoice_date' => $nextInvoiceDate,
            'status' => 'active',
            'invoice_data' => $data['invoice_data'] ?? [],
            'created_by' => auth()->id(),
        ]);
    }

    /**
     * Generate invoices from recurring invoices
     */
    public function generateInvoices(): int
    {
        $count = 0;
        $recurringInvoices = RecurringInvoice::where('status', 'active')
            ->where('next_invoice_date', '<=', now())
            ->get();

        foreach ($recurringInvoices as $recurring) {
            try {
                $this->generateInvoice($recurring);
                $count++;
            } catch (Exception $e) {
                Log::error("Failed to generate invoice for recurring {$recurring->id}: ".$e->getMessage());
            }
        }

        return $count;
    }

    /**
     * Generate a single invoice from recurring invoice
     */
    private function generateInvoice(RecurringInvoice $recurring): void
    {
        DB::beginTransaction();

        try {
            $invoiceData = $recurring->invoice_data;
            $invoiceData['customer_id'] = $recurring->customer_id;
            $invoiceData['issue_date'] = $recurring->next_invoice_date;
            $invoiceData['due_date'] = $this->calculateDueDate($recurring->next_invoice_date);
            $invoiceData['recurring_invoice_id'] = $recurring->id;
            $invoiceData['status'] = 'sent';

            // Create invoice
            $invoice = $this->invoiceService->create($invoiceData);
            $this->invoiceService->markAsSent($invoice);

            // Update recurring invoice
            $this->updateNextInvoiceDate($recurring);

            DB::commit();

            try {
                $this->reminderService->sendRecurringInvoiceGenerated($invoice->fresh(['customer']));
            } catch (\Throwable) {
            }
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    /**
     * Calculate next invoice date
     */
    private function calculateNextInvoiceDate(string $startDate, string $frequency): Carbon
    {
        $date = Carbon::parse($startDate);

        return match ($frequency) {
            'daily' => $date->addDay(),
            'weekly' => $date->addWeek(),
            'biweekly' => $date->addWeeks(2),
            'monthly' => $date->addMonth(),
            'quarterly' => $date->addMonths(3),
            'semiannually' => $date->addMonths(6),
            'annually' => $date->addYear(),
            default => $date->addMonth(),
        };
    }

    /**
     * Calculate due date (30 days from invoice date)
     */
    private function calculateDueDate(Carbon $invoiceDate): Carbon
    {
        return $invoiceDate->copy()->addDays(30);
    }

    /**
     * Update next invoice date for recurring invoice
     */
    private function updateNextInvoiceDate(RecurringInvoice $recurring): void
    {
        $nextDate = $this->calculateNextInvoiceDate(
            $recurring->next_invoice_date->toDateString(),
            $recurring->frequency
        );

        // Check if we've reached end date or max occurrences
        $shouldStop = false;

        if ($recurring->end_date && $nextDate->gt($recurring->end_date)) {
            $shouldStop = true;
        }

        if ($recurring->occurrences) {
            $generatedCount = $recurring->invoices()->count();
            if ($generatedCount >= $recurring->occurrences) {
                $shouldStop = true;
            }
        }

        if ($shouldStop) {
            $recurring->update([
                'status' => 'completed',
                'next_invoice_date' => null,
            ]);
        } else {
            $recurring->update([
                'next_invoice_date' => $nextDate,
            ]);
        }
    }
}
