<?php

namespace App\Core\Services\Quotation;

use App\Core\Repositories\CustomerRepository;
use App\Core\Repositories\QuotationRepository;
use App\Models\Quotation;
use App\Models\QuotationItem;
use App\Support\QuotationTemplateResolver;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Quotation Service
 * Handles all quotation business logic
 */
class QuotationService
{
    public function __construct(
        private QuotationRepository $quotationRepository,
        private CustomerRepository $customerRepository
    ) {}

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

        try {
            // Generate quotation number if not provided
            if (empty($data['quotation_number'])) {
                $data['quotation_number'] = $this->quotationRepository->generateQuotationNumber();
            }

            // 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\Quotation::where('public_token', $publicToken)->exists()) {
                $publicToken = \Illuminate\Support\Str::random(32);
            }

            // Set expiry date if not provided
            if (empty($data['expiry_date']) && ! empty($data['issue_date'])) {
                $validityDays = \App\Models\QuotationSetting::getSettings()->validity_days ?? 30;
                $data['expiry_date'] = \Carbon\Carbon::parse($data['issue_date'])->addDays($validityDays);
            }

            // Create quotation
            $quotation = $this->quotationRepository->create([
                'quotation_number' => $data['quotation_number'],
                'public_token' => $publicToken,
                'customer_id' => $data['customer_id'],
                'issue_date' => $data['issue_date'],
                'expiry_date' => $data['expiry_date'] ?? null,
                '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'],
                'currency' => $data['currency'] ?? \App\Models\Currency::getDefaultCode(),
                'template' => isset($data['template']) && $data['template'] !== ''
                    ? QuotationTemplateResolver::normalize($data['template'])
                    : QuotationTemplateResolver::resolveDefaultKey(),
                'exchange_rate' => $data['exchange_rate'] ?? 1,
                'notes' => $data['notes'] ?? null,
                'terms' => $data['terms'] ?? null,
                'reference_number' => $data['reference_number'] ?? null,
                'sales_person' => $data['sales_person'] ?? null,
                'created_by' => auth()->id(),
            ]);

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

            DB::commit();

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

    /**
     * Update a quotation
     */
    public function update(Quotation $quotation, array $data): Quotation
    {
        DB::beginTransaction();

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

            // Update quotation
            $updateRow = [
                'customer_id' => $data['customer_id'] ?? $quotation->customer_id,
                'issue_date' => $data['issue_date'] ?? $quotation->issue_date,
                'expiry_date' => $data['expiry_date'] ?? $quotation->expiry_date,
                'status' => $data['status'] ?? $quotation->status,
                'subtotal' => $totals['subtotal'],
                'tax_amount' => $totals['tax_amount'],
                'discount_amount' => $totals['discount_amount'] ?? 0,
                'discount_type' => $data['discount_type'] ?? $quotation->discount_type,
                'total' => $totals['total'],
                'currency' => $data['currency'] ?? $quotation->currency,
                'exchange_rate' => $data['exchange_rate'] ?? $quotation->exchange_rate,
                'notes' => $data['notes'] ?? $quotation->notes,
                'terms' => $data['terms'] ?? $quotation->terms,
                'reference_number' => $data['reference_number'] ?? $quotation->reference_number,
                'sales_person' => $data['sales_person'] ?? $quotation->sales_person,
            ];
            if (array_key_exists('template', $data)) {
                $updateRow['template'] = $data['template'] === '' || $data['template'] === null
                    ? null
                    : QuotationTemplateResolver::normalize($data['template']);
            }
            $this->quotationRepository->update($quotation, $updateRow);

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

            DB::commit();

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

    /**
     * Delete a quotation
     */
    public function delete(Quotation $quotation): bool
    {
        return $this->quotationRepository->delete($quotation);
    }

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

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

        return true;
    }

    /**
     * Convert quotation to invoice
     */
    public function convertToInvoice(Quotation $quotation, array $invoiceData = []): \App\Models\Invoice
    {
        DB::beginTransaction();

        try {
            // Use InvoiceService to create invoice
            $invoiceService = app(\App\Core\Services\Invoice\InvoiceService::class);

            $invoiceData = array_merge([
                'customer_id' => $quotation->customer_id,
                'issue_date' => $quotation->issue_date,
                'due_date' => $invoiceData['due_date'] ?? now()->addDays(30),
                'currency' => $quotation->currency,
                'exchange_rate' => $quotation->exchange_rate,
                'notes' => $quotation->notes,
                'terms' => $quotation->terms,
                'items' => $quotation->items->map(function ($item) {
                    return [
                        'description' => $item->description,
                        'quantity' => $item->quantity,
                        'unit_price' => $item->unit_price,
                        'tax_rate' => $item->tax_rate,
                        'notes' => $item->notes,
                    ];
                })->toArray(),
            ], $invoiceData);

            $invoice = $invoiceService->create($invoiceData);

            // Mark quotation as converted
            $this->quotationRepository->update($quotation, [
                'status' => 'converted',
                'converted_to_invoice_id' => $invoice->id,
            ]);

            DB::commit();

            return $invoice;
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Quotation conversion failed: '.$e->getMessage());
            throw $e;
        }
    }

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

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

            if (isset($item['unit'])) {
                $itemData['unit'] = $item['unit'];
            }

            if (isset($item['notes'])) {
                $itemData['notes'] = $item['notes'];
            }

            QuotationItem::create($itemData);
        }
    }

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

        foreach ($items as $item) {
            $itemSubtotal = ($item['quantity'] ?? 0) * ($item['unit_price'] ?? 0);
            $itemTax = $itemSubtotal * (($item['tax_rate'] ?? 0) / 100);

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

        // Apply discount
        $discount = 0;
        if ($discountAmount) {
            if ($discountType === 'percentage') {
                $discount = $subtotal * ($discountAmount / 100);
            } else {
                $discount = $discountAmount;
            }
        }

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

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