<?php

namespace PortalPeople\invoicing\services;

use backend\models\Cartsbe;
use PortalPeople\invoicing\models\Invoicesbe;
use Yii;

/**
 * Invoice Generation Service
 *
 * Orchestrates invoice creation from eligible carts with idempotency and error handling.
 */
class InvoiceGenerationService
{
    protected InvoiceEligibilityService $eligibilityService;
    protected InvoiceFactory $factory;

    public function __construct(
        ?InvoiceEligibilityService $eligibilityService = null,
        ?InvoiceFactory $factory = null
    ) {
        $this->eligibilityService = $eligibilityService ?? new InvoiceEligibilityService();
        $this->factory = $factory ?? new InvoiceFactory();
    }

    /**
     * Generate invoices for eligible carts
     *
     * @param string|null $asOfDate Date to filter by (YYYY-MM-DD)
     * @param bool $dryRun If true, do not actually save invoices
     * @return array Result statistics
     */
    public function generate(?string $asOfDate = null, bool $dryRun = false): array
    {
        $asOfDate = $asOfDate ?? date('Y-m-d');
        
        $results = [
            'created' => 0,
            'skipped' => 0,
            'failed' => 0,
            'errors' => [],
            'dry_run' => $dryRun,
            'date' => $asOfDate,
        ];

        $eligibleCarts = $this->eligibilityService->getEligibleCartsQuery($asOfDate)->all();

        foreach ($eligibleCarts as $cart) {
            try {
                $key = $this->factory->generateUniqueKey($cart);
                
                // Check if already invoiced (idempotency)
                $existing = Invoicesbe::findOne(['generation_key' => $key]);
                if ($existing) {
                    $results['skipped']++;
                    continue;
                }

                // Create invoice snapshot
                $invoiceData = $this->factory->createInvoiceDataFromCart($cart);
                $invoice = new Invoicesbe();
                
                foreach ($invoiceData as $attribute => $value) {
                    if ($invoice->hasAttribute($attribute)) {
                        $invoice->{$attribute} = $value;
                    }
                }

                // Validate invoice
                if (!$invoice->validate()) {
                    $results['failed']++;
                    $results['errors'][] = [
                        'cart_id' => $cart->id,
                        'errors' => $invoice->getErrors(),
                    ];
                    continue;
                }

                if (!$dryRun) {
                    // Save in transaction
                    $transaction = Yii::$app->db->beginTransaction();
                    try {
                        if (!$invoice->save()) {
                            throw new \Exception('Failed to save invoice: ' . json_encode($invoice->getErrors()));
                        }

                        // Link invoice back to cart
                        $cart->invoiceid = $invoice->id;
                        $cart->invoiced_by = Yii::$app->user->identity?->id ?? 'console';
                        $cart->invoicedate = time();
                        
                        if (!$cart->save(false)) {
                            throw new \Exception('Failed to update cart linkage: ' . json_encode($cart->getErrors()));
                        }

                        $transaction->commit();
                        $results['created']++;
                    } catch (\Exception $e) {
                        $transaction->rollBack();
                        $results['failed']++;
                        $results['errors'][] = [
                            'cart_id' => $cart->id,
                            'exception' => $e->getMessage(),
                        ];
                    }
                } else {
                    $results['created']++;
                }

            } catch (\Exception $e) {
                $results['failed']++;
                $results['errors'][] = [
                    'cart_id' => $cart->id ?? 'unknown',
                    'exception' => $e->getMessage(),
                ];
            }
        }

        return $results;
    }
}
