<?php

namespace Modules\POS\Controllers;

use core\Controller;
use core\Auth;
use core\Database;
use core\Session;
use core\Security;
use Modules\POS\Models\Invoice;
use Modules\POS\Models\Payment;

class POSController extends Controller
{
    private Invoice $invoiceModel;
    private Payment $paymentModel;

    public function __construct()
    {
        parent::__construct();
        $this->invoiceModel = new Invoice();
        $this->paymentModel = new Payment();
    }

    public function index(): void
    {
        $this->requirePermission('pos_view');

        $db = Database::getInstance();
        $categories = $db->table('service_categories')
            ->where('is_active', 1)
            ->where('deleted_at', 'IS', null)
            ->orderBy('sort_order', 'ASC')
            ->get();

        $services = $db->table('services s')
            ->select('s.*, sc.name as category_name, sc.id as category_id')
            ->leftJoin('service_categories sc', 's.category_id', '=', 'sc.id')
            ->where('s.is_active', 1)
            ->where('s.deleted_at', 'IS', null)
            ->orderBy('s.name', 'ASC')
            ->get();

        $products = $db->table('products')
            ->where('is_active', 1)
            ->where('deleted_at', 'IS', null)
            ->orderBy('name', 'ASC')
            ->get();

        $employees = $db->table('employees')
            ->where('is_active', 1)
            ->where('deleted_at', 'IS', null)
            ->orderBy('first_name', 'ASC')
            ->get();

        $customers = $db->table('customers')
            ->where('deleted_at', 'IS', null)
            ->orderBy('first_name', 'ASC')
            ->get();

        $cart = Session::get('pos_cart', []);

        $this->view('POS/pos', [
            'title' => 'Point of Sale',
            'breadcrumb' => ['POS' => url('pos')],
            'categories' => $categories,
            'services' => $services,
            'products' => $products,
            'employees' => $employees,
            'customers' => $customers,
            'cart' => $cart,
        ]);
    }

    public function newSale(): void
    {
        $this->requirePermission('pos_create');
        $this->redirect(url('pos'));
    }

    public function calculate(): void
    {
        $this->requirePermission('pos_view');

        $cartRaw = $this->request->post('cart_items', '[]');
        $cart = json_decode($cartRaw, true) ?: [];
        $discountType = $this->request->post('discount_type', 'percentage');
        $discountValue = (float)$this->request->post('discount_value', 0);
        $couponDiscount = (float)$this->request->post('coupon_discount', 0);

        $subtotal = 0;
        foreach ($cart as $item) {
            $subtotal += (float)$item['price'] * (int)$item['quantity'];
        }

        $discount = 0;
        if ($discountType === 'percentage' && $discountValue > 0) {
            $discount = ($subtotal * $discountValue) / 100;
        } elseif ($discountType === 'fixed' && $discountValue > 0) {
            $discount = min($discountValue, $subtotal);
        }

        $discount += $couponDiscount;
        $discount = min($discount, $subtotal);

        $taxable = $subtotal - $discount;
        $taxRate = APP_TAX_RATE > 0 ? APP_TAX_RATE : (float)$this->request->post('tax_rate', 0);
        $tax = ($taxable * $taxRate) / 100;

        $grandTotal = $taxable + $tax;

        $this->success([
            'subtotal' => round($subtotal, 2),
            'discount' => round($discount, 2),
            'tax' => round($tax, 2),
            'grand_total' => round($grandTotal, 2),
            'item_count' => array_sum(array_column($cart, 'quantity')),
        ]);
    }

    public function checkout(): void
    {
        $this->requirePermission('pos_create');

        $customerId = $this->request->post('customer_id') ? (int)$this->request->post('customer_id') : 3; // Walk-in
        $cartRaw = $this->request->post('cart_items', '[]');
        $cart = json_decode($cartRaw, true) ?: [];
        $discountType = $this->request->post('discount_type', 'percentage');
        $discountValue = (float)$this->request->post('discount_value', 0);
        $couponDiscount = (float)$this->request->post('coupon_discount', 0);
        $taxRate = (float)$this->request->post('tax_rate', APP_TAX_RATE);
        $note = $this->request->post('note', '');
        $payments = $this->request->post('payments', []);
        $couponCode = $this->request->post('coupon_code', '');
        $appointmentId = (int)$this->request->post('appointment_id', 0) ?: null;

        if (empty($cart)) {
            $this->error('Cart is empty.');
        }

        if (empty($payments)) {
            $this->error('At least one payment method is required.');
        }

        $subtotal = 0;
        foreach ($cart as $item) {
            $subtotal += (float)$item['price'] * (int)$item['quantity'];
        }

        $discount = 0;
        if ($discountType === 'percentage' && $discountValue > 0) {
            $discount = ($subtotal * $discountValue) / 100;
        } elseif ($discountType === 'fixed' && $discountValue > 0) {
            $discount = min($discountValue, $subtotal);
        }
        $discount += $couponDiscount;
        $discount = min($discount, $subtotal);

        $taxable = $subtotal - $discount;
        $tax = ($taxable * $taxRate) / 100;
        $grandTotal = $taxable + $tax;

        $totalPaid = 0;
        foreach ($payments as $payment) {
            $totalPaid += (float)($payment['amount'] ?? 0);
        }

        if ($totalPaid < $grandTotal) {
            $this->error('Total payment amount must equal or exceed the grand total.');
        }

        $paymentStatus = $totalPaid >= $grandTotal ? 'paid' : 'partial';
        $balance = max(0, $grandTotal - $totalPaid);
        $changeDue = max(0, $totalPaid - $grandTotal);

        $db = Database::getInstance();
        $db->beginTransaction();

        try {
            $invoiceNumber = $this->invoiceModel->generateInvoiceNumber();

            $invoiceData = [
                'invoice_number' => $invoiceNumber,
                'customer_id' => $customerId,
                'appointment_id' => $appointmentId,
                'created_by' => Auth::id(),
                'branch_id' => Auth::branchId(),
                'subtotal' => $subtotal,
                'discount_total' => $discount,
                'tax_total' => $tax,
                'grand_total' => $grandTotal,
                'net_total' => $grandTotal,
                'paid_amount' => $totalPaid,
                'balance_amount' => $balance,
                'payment_status' => $paymentStatus,
                'notes' => $note,
                'created_at' => date(DATE_TIME_FORMAT),
                'updated_at' => date(DATE_TIME_FORMAT),
            ];

            $invoiceId = $db->table('invoices')->insert($invoiceData);

            foreach ($cart as $item) {
                $refType = $item['type'] === 'services' ? 'service' : 'product';
                $lineTotal = (float)$item['price'] * (int)$item['quantity'];
                $db->table('invoice_items')->insert([
                    'invoice_id' => $invoiceId,
                    'reference_type' => $refType,
                    'reference_id' => (int)$item['id'],
                    'item_name' => $item['name'],
                    'quantity' => (int)$item['quantity'],
                    'unit_price' => (float)$item['price'],
                    'total_price' => $lineTotal,
                    'employee_id' => !empty($item['employee_id']) ? (int)$item['employee_id'] : null,
                    'created_at' => date(DATE_TIME_FORMAT),
                ]);
            }

            foreach ($payments as $payment) {
                $db->table('invoice_payments')->insert([
                    'invoice_id' => $invoiceId,
                    'payment_method' => $payment['method'],
                    'amount' => (float)$payment['amount'],
                    'reference_number' => $payment['reference'] ?? null,
                    'created_at' => date(DATE_TIME_FORMAT),
                ]);
            }

            if (!empty($couponCode)) {
                $coupon = $db->table('coupons')->where('code', $couponCode)->first();
                if ($coupon) {
                    $db->table('coupons')
                        ->where('id', $coupon->id)
                        ->increment('used_count', 1);
                }
            }

            $db->commit();

            Session::remove('pos_cart');
            Session::remove('pos_customer_id');

            $this->logActivity('create', 'Created invoice #' . $invoiceNumber);
            $this->logAudit('create', 'invoices', $invoiceId, null, $invoiceData);

            $this->success(['invoice_id' => $invoiceId, 'invoice_number' => $invoiceNumber], 'Invoice created successfully.');
        } catch (\Throwable $e) {
            $db->rollback();
            $this->error('Failed to process checkout: ' . $e->getMessage());
        }
    }

    public function validateCoupon(): void
    {
        $this->requirePermission('pos_view');

        $code = $this->request->post('code', '');
        if (empty($code)) {
            $this->error('Coupon code is required.');
        }

        $db = Database::getInstance();
        $coupon = $db->table('coupons')
            ->where('code', $code)
            ->where('is_active', 1)
            ->first();

        if (!$coupon) {
            $this->error('Invalid coupon code.');
        }

        $now = date('Y-m-d H:i:s');
        if ($coupon->valid_from && $now < $coupon->valid_from) {
            $this->error('Coupon is not yet valid.');
        }
        if ($coupon->valid_to && $now > $coupon->valid_to) {
            $this->error('Coupon has expired.');
        }
        if ($coupon->max_uses > 0 && $coupon->used_count >= $coupon->max_uses) {
            $this->error('Coupon usage limit exceeded.');
        }

        $discountAmount = 0;
        $subtotal = (float)$this->request->post('subtotal', 0);

        if ($coupon->type === 'percentage') {
            $discountAmount = ($subtotal * (float)$coupon->value) / 100;
            if ((float)$coupon->max_discount > 0) {
                $discountAmount = min($discountAmount, (float)$coupon->max_discount);
            }
        } else {
            $discountAmount = min((float)$coupon->value, $subtotal);
        }

        if ((float)$coupon->min_order > 0 && $subtotal < (float)$coupon->min_order) {
            $this->error('Minimum order amount of ' . currency((float)$coupon->min_order) . ' required.');
        }

        $this->success([
            'coupon_id' => $coupon->id,
            'code' => $coupon->code,
            'discount' => round($discountAmount, 2),
            'type' => $coupon->type,
            'value' => (float)$coupon->value,
            'description' => $coupon->description ?? '',
        ], 'Coupon applied successfully.');
    }

    public function loadAppointment(int $id): void
    {
        $this->requirePermission('pos_view');

        $db = Database::getInstance();
        $appointment = $db->query(
            "SELECT a.id, a.customer_id, a.start_time, a.end_time, a.status,
                    c.first_name as customer_first_name, c.last_name as customer_last_name
             FROM appointments a
             INNER JOIN customers c ON a.customer_id = c.id
             WHERE a.id = :id AND a.deleted_at IS NULL",
            [':id' => $id]
        );

        if (empty($appointment)) {
            $this->error('Appointment not found.', 404);
        }
        $appointment = $appointment[0];

        $existingInvoice = $db->table('invoices')
            ->where('appointment_id', $id)
            ->where('deleted_at', 'IS', null)
            ->first();

        if ($existingInvoice) {
            $this->success([
                'existing_invoice_id' => $existingInvoice->id,
                'existing_invoice_number' => $existingInvoice->invoice_number,
                'appointment_id' => $appointment->id,
            ]);
            return;
        }

        $services = $db->query(
            "SELECT aps.id, aps.service_id, aps.employee_id, aps.price, aps.duration_minutes,
                    s.name as service_name, s.price as service_price,
                    CONCAT(e.first_name, ' ', e.last_name) as employee_name
             FROM appointment_services aps
             INNER JOIN services s ON aps.service_id = s.id
             LEFT JOIN employees e ON aps.employee_id = e.id
             WHERE aps.appointment_id = :id",
            [':id' => $id]
        );

        $cart = [];
        foreach ($services as $svc) {
            $key = 'services_' . $svc->service_id;
            $cart[$key] = [
                'type' => 'services',
                'id' => (int)$svc->service_id,
                'name' => $svc->service_name,
                'price' => (float)((float)$svc->price ?: $svc->service_price),
                'quantity' => 1,
                'employee_id' => $svc->employee_id ? (string)$svc->employee_id : '',
            ];
        }

        $this->success([
            'customer_id' => $appointment->customer_id,
            'customer_name' => $appointment->customer_first_name . ' ' . $appointment->customer_last_name,
            'appointment_id' => $appointment->id,
            'cart' => $cart,
        ]);
    }

    public function invoice(int $id): void
    {
        $this->requirePermission('pos_invoice_view');

        $db = Database::getInstance();
        $invoice = $db->query(
            "SELECT i.*, c.first_name as customer_first_name, c.last_name as customer_last_name,
                    c.email as customer_email, c.phone as customer_phone, c.address as customer_address,
                    u.first_name as user_first_name, u.last_name as user_last_name,
                    b.name as branch_name, b.address as branch_address, b.phone as branch_phone, b.email as branch_email
             FROM invoices i
             LEFT JOIN customers c ON i.customer_id = c.id
             LEFT JOIN users u ON i.created_by = u.id
             LEFT JOIN branches b ON i.branch_id = b.id
             WHERE i.id = :id AND i.deleted_at IS NULL",
            [':id' => $id]
        );

        if (empty($invoice)) {
            $this->redirectWith(url('invoices'), 'error', 'Invoice not found.');
        }
        $invoice = $invoice[0];

        $items = $db->table('invoice_items')
            ->where('invoice_id', $id)
            ->get();

        $payments = $db->table('invoice_payments')
            ->where('invoice_id', $id)
            ->get();

        $this->view('POS/invoice', [
            'title' => 'Invoice #' . $invoice->invoice_number,
            'breadcrumb' => [
                'POS' => url('pos'),
                'Invoices' => url('invoices'),
                '#' . $invoice->invoice_number => url('invoices/' . $id),
            ],
            'invoice' => $invoice,
            'items' => $items,
            'payments' => $payments,
        ]);
    }

    public function printInvoice(int $id): void
    {
        $this->requirePermission('pos_invoice_view');

        $db = Database::getInstance();
        $invoice = $db->query(
            "SELECT i.*, c.first_name as customer_first_name, c.last_name as customer_last_name,
                    c.email as customer_email, c.phone as customer_phone, c.address as customer_address,
                    u.first_name as user_first_name, u.last_name as user_last_name,
                    b.name as branch_name, b.address as branch_address, b.phone as branch_phone, b.email as branch_email
             FROM invoices i
             LEFT JOIN customers c ON i.customer_id = c.id
             LEFT JOIN users u ON i.created_by = u.id
             LEFT JOIN branches b ON i.branch_id = b.id
             WHERE i.id = :id AND i.deleted_at IS NULL",
            [':id' => $id]
        );

        if (empty($invoice)) {
            $this->redirectWith(url('invoices'), 'error', 'Invoice not found.');
        }
        $invoice = $invoice[0];

        $items = $db->table('invoice_items')
            ->where('invoice_id', $id)
            ->get();

        $payments = $db->table('invoice_payments')
            ->where('invoice_id', $id)
            ->get();

        $this->view('POS/print_invoice', [
            'title' => 'Print Invoice #' . $invoice->invoice_number,
            'invoice' => $invoice,
            'items' => $items,
            'payments' => $payments,
        ], 'none');
    }
}
