<?php

namespace App\Core\Services\Invoice;

use App\Core\Repositories\CustomerRepository;
use App\Core\Repositories\InvoiceRepository;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Support\InvoiceTemplateResolver;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Invoice Service
 * Handles all invoice business logic
 */
class InvoiceService
{
    public function __construct(
        private InvoiceRepository $invoiceRepository,
        private CustomerRepository $customerRepository
    ) {}

    /**
     * Create a new invoice
     */
    public function create(array $data): Invoice
    {
        DB::beginTransaction();

        try {
            // Generate invoice number if not provided
            if (empty($data['invoice_number'])) {
                $data['invoice_number'] = $this->invoiceRepository->generateInvoiceNumber();
            }

            // Calculate totals
            $totals = $this->calculateTotals(
                $data['items'] ?? [],
                $data['discount_amount'] ?? null,
                $data['discount_type'] ?? null
            );

            // Generate public token
            $publicToken = \Illuminate\Support\Str::random(32);

            // Ensure token is unique
            while (\App\Models\Invoice::where('public_token', $publicToken)->exists()) {
                $publicToken = \Illuminate\Support\Str::random(32);
            }

            // Create invoice
            $invoice = $this->invoiceRepository->create([
                'invoice_number' => $data['invoice_number'],
                'public_token' => $publicToken,
                'customer_id' => $data['customer_id'],
                'issue_date' => $data['issue_date'],
                'due_date' => $data['due_date'],
                'status' => $data['status'] ?? 'draft',
                'subtotal' => $totals['subtotal'],
                'tax_amount' => $totals['tax_amount'],
                'discount_amount' => $totals['discount_amount'] ?? 0,
                'discount_type' => $data['discount_type'] ?? null,
                'total' => $totals['total'],
                'paid_amount' => $data['paid_amount'] ?? 0,
                'balance' => max(0, $totals['total'] - ($data['paid_amount'] ?? 0)),
                'currency' => $data['currency'] ?? \App\Models\Currency::getDefaultCode(),
                'template' => ! empty($data['template'] ?? null)
                    ? InvoiceTemplateResolver::normalize($data['template'])
                    : null,
                'exchange_rate' => $data['exchange_rate'] ?? 1,
                'notes' => $data['notes'] ?? null,
                'terms' => $data['terms'] ?? null,
                'payment_method' => $data['payment_method'] ?? null,
                'recurring_invoice_id' => $data['recurring_invoice_id'] ?? null,
                'created_by' => auth()->id(),
            ]);

            // Create invoice items
            if (isset($data['items']) && is_array($data['items'])) {
                $this->createInvoiceItems($invoice, $data['items']);
            }

            DB::commit();

            return $invoice->load(['customer', 'items']);
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Invoice creation failed: '.$e->getMessage());
            throw $e;
        }
    }

    /**
     * Update an invoice
     */
    public function update(Invoice $invoice, array $data): Invoice
    {
        DB::beginTransaction();

        try {
            // Calculate totals
            $totals = $this->calculateTotals(
                $data['items'] ?? [],
                $data['discount_amount'] ?? $invoice->discount_amount,
                $data['discount_type'] ?? $invoice->discount_type
            );

            // Update invoice
            $this->invoiceRepository->update($invoice, [
                'customer_id' => $data['customer_id'] ?? $invoice->customer_id,
                'issue_date' => $data['issue_date'] ?? $invoice->issue_date,
                'due_date' => $data['due_date'] ?? $invoice->due_date,
                'status' => $data['status'] ?? $invoice->status,
                'subtotal' => $totals['subtotal'],
                'tax_amount' => $totals['tax_amount'],
                'discount_amount' => $totals['discount_amount'] ?? 0,
                'discount_type' => $data['discount_type'] ?? $invoice->discount_type,
                'total' => $totals['total'],
                'balance' => $totals['total'] - $invoice->paid_amount,
                'currency' => $data['currency'] ?? $invoice->currency,
                'template' => array_key_exists('template', $data)
                    ? (! empty($data['template']) ? InvoiceTemplateResolver::normalize($data['template']) : null)
                    : $invoice->template,
                'exchange_rate' => $data['exchange_rate'] ?? $invoice->exchange_rate,
                'notes' => $data['notes'] ?? $invoice->notes,
                'terms' => $data['terms'] ?? $invoice->terms,
                'payment_method' => $data['payment_method'] ?? $invoice->payment_method,
            ]);

            // Update items
            if (isset($data['items']) && is_array($data['items'])) {
                $invoice->items()->delete();
                $this->createInvoiceItems($invoice, $data['items']);
            }

            DB::commit();

            return $invoice->fresh(['customer', 'items']);
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Invoice update failed: '.$e->getMessage());
            throw $e;
        }
    }

    /**
     * Delete an invoice
     */
    public function delete(Invoice $invoice): bool
    {
        return $this->invoiceRepository->delete($invoice);
    }

    /**
     * Mark invoice as sent
     */
    public function markAsSent(Invoice $invoice): bool
    {
        return $this->invoiceRepository->update($invoice, [
            'status' => 'sent',
            'sent_at' => now(),
        ]);
    }

    /**
     * Mark invoice as viewed
     */
    public function markAsViewed(Invoice $invoice): bool
    {
        if (! $invoice->viewed_at) {
            return $this->invoiceRepository->update($invoice, [
                'status' => 'viewed',
                'viewed_at' => now(),
            ]);
        }

        return true;
    }

    /**
     * Create invoice items
     */
    private function createInvoiceItems(Invoice $invoice, array $items): void
    {
        foreach ($items as $index => $item) {
            $subtotal = $item['quantity'] * $item['unit_price'];
            $taxAmount = $subtotal * ($item['tax_rate'] ?? 0) / 100;
            $total = $subtotal + $taxAmount;

            $itemData = [
                'invoice_id' => $invoice->id,
                'description' => $item['description'],
                'quantity' => $item['quantity'],
                'unit_price' => $item['unit_price'],
                'tax_rate' => $item['tax_rate'] ?? 0,
                'tax_amount' => $taxAmount,
                'total' => $total,
                'sort_order' => $index,
            ];

            // Only add notes if column exists
            if (\Schema::hasColumn('invoice_items', 'notes')) {
                $itemData['notes'] = $item['notes'] ?? null;
            }

            InvoiceItem::create($itemData);
        }
    }

    /**
     * Calculate invoice totals
     */
    private function calculateTotals(array $items, $discountAmount = null, ?string $discountType = null): array
    {
        $subtotal = 0;
        $taxAmount = 0;

        foreach ($items as $item) {
            $quantity = floatval($item['quantity'] ?? 0);
            $unitPrice = floatval($item['unit_price'] ?? 0);
            $taxRate = floatval($item['tax_rate'] ?? 0);

            $itemSubtotal = $quantity * $unitPrice;
            $itemTax = $itemSubtotal * ($taxRate / 100);

            $subtotal += $itemSubtotal;
            $taxAmount += $itemTax;
        }

        // Apply discount - ensure proper type casting and bounds checking
        $discount = 0;
        if ($discountAmount !== null && $discountAmount !== '') {
            $discountAmount = floatval($discountAmount);

            // Ensure discount amount is within reasonable bounds
            if ($discountAmount < 0) {
                $discountAmount = 0;
            }
            if ($discountAmount > 999999999.99) {
                $discountAmount = 999999999.99;
            }

            if ($discountType === 'percentage') {
                // Ensure percentage is between 0 and 100
                if ($discountAmount > 100) {
                    $discountAmount = 100;
                }
                $discount = $subtotal * ($discountAmount / 100);
            } else {
                // For fixed discount, ensure it doesn't exceed subtotal
                $discount = min($discountAmount, $subtotal);
            }
        }

        $total = $subtotal + $taxAmount - $discount;

        return [
            'subtotal' => round($subtotal, 2),
            'tax_amount' => round($taxAmount, 2),
            'discount_amount' => round($discount, 2),
            'total' => max(0, round($total, 2)),
        ];
    }
}
