<?php

namespace App\Http\Controllers;

use App\Models\DocumentHeader;
use App\Models\DocumentLine;
use App\Models\CustomerAccount;
use App\Models\PriceListingSetup;
use App\Models\SupplierDocumentLine;
use App\Models\GeneralLedger;
use App\Models\ItemWarehouse;
use App\Models\Warehouse;
use App\Models\Project;
use App\Models\Tax;
use App\Models\Currency;
use App\Models\Uom;
use App\Models\SalesRep;
use App\Models\DocumentSetting;
use App\Models\CompanySettings;
use App\Models\Receipt;
use App\Models\ReceiptLine;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Barryvdh\DomPDF\Facade\Pdf as PDF;

class DocumentController extends Controller
{
    public function create($type)
    {
        $themeColors = [
            'quotation'    => ['color' => '#018ca3', 'light' => '#5ab8cc', 'dark' => '#006175'],
            'sales_order'  => ['color' => '#28a745', 'light' => '#5cb85c', 'dark' => '#1e7e34'],
            'invoice'      => ['color' => '#001f3f', 'light' => '#5D8AA8', 'dark' => '#001a33'],
            'credit_note'  => ['color' => '#dc3545', 'light' => '#e4606d', 'dark' => '#bd2130'],
            'debit_note'   => ['color' => '#07d7fcff', 'light' => '#4db1f4ff', 'dark' => '#02bff8ff'],
            'receipt'      => ['color' => '#6f42c1', 'light' => '#9d7bd9', 'dark' => '#563d7c'],
        ];

        $theme = $themeColors[$type] ?? $themeColors['quotation'];

        DocumentSetting::firstOrCreate(
            ['document_type' => $type],
            ['prefix' => $this->getDefaultPrefix($type), 'start_number' => 1, 'pricing_type' => 'exclusive']
        );

        $settings    = DocumentSetting::where('document_type', $type)->first();
        $pricingType = $settings->pricing_type ?? session('pricing_type', 'exclusive');

        $documentNumber = $this->generateDocumentNumber($type, false);
        $uoms           = Uom::all();
        $UOM            = $uoms->first();

        return view('maintanance.process.create_customerquot', [
            'type'               => $type,
            'documentTitle'      => $this->getDocumentTitle($type),
            'customers'          => CustomerAccount::all(),
            'items'              => $this->getItems(),
            'accounts'           => GeneralLedger::with('subAccounts')->get(),
            'warehouses'         => Warehouse::all(),
            'projects'           => Project::all(),
            'taxes'              => Tax::all(),
            'currencies'         => Currency::all(),
            'uoms'               => $uoms,
            'UOM'                => $UOM,
            'salesReps'          => SalesRep::all(),
            'documentNumber'     => $documentNumber,
            'showNegativeOption' => in_array($type, ['invoice','credit_note','debit_note','receipt']),
            'themeColor'         => $theme['color'],
            'themeLight'         => $theme['light'],
            'themeDark'          => $theme['dark'],
            'pricingType'        => $pricingType,
            'showDueDateField'   => $type === 'invoice',
        ]);
    }

    private function getDocumentTitle($type)
    {
        return [
            'quotation'   => 'Quotation',
            'sales_order' => 'Sales Order',
            'invoice'     => 'Invoice',
            'credit_note' => 'Credit Note',
            'debit_note'  => 'Debit Note',
            'receipt'     => 'Receipt',
        ][$type] ?? 'Document';
    }

    private function getDefaultPrefix($type)
    {
        return [
            'quotation'   => 'QT',
            'sales_order' => 'SO',
            'invoice'     => 'INV',
            'credit_note' => 'CN',
            'debit_note'  => 'DN',
            'receipt'     => 'RCPT',
        ][$type] ?? 'DOC';
    }

    private function getItems()
    {
        $availableQuantities = SupplierDocumentLine::select('item_id', DB::raw('SUM(confirmed_quantity) as total_available'))
            ->whereHas('header', function($query) {
                $query->where('type', 'goods_received');
            })
            ->groupBy('item_id')
            ->pluck('total_available', 'item_id');

        return PriceListingSetup::with(['stockingUnit','warehouse','itemWarehouses'])
            ->get()
            ->map(function($item) use ($availableQuantities) {
                $availableFromGRV = $availableQuantities->get($item->id, 0);
                $stockFromWarehouses = $item->itemWarehouses->sum('quantity');
                $available = $stockFromWarehouses;

                return [
                    'id'             => $item->id,
                    'code'           => $item->code,
                    'description1'   => $item->description1,
                    'description2'   => $item->description2,
                    'warehouse_id'   => $item->warehouse_id,
                    'warehouse_desc' => optional($item->warehouse)->description ?? '',
                    'uom'            => optional($item->stockingUnit)->code ?? '',
                    'cost'           => $item->cost,
                    'available'      => $available,
                ];
            });
    }

    private function generateDocumentNumber(string $type, bool $increment = true): string
    {
        return DB::transaction(function () use ($type, $increment) {
            $settings = DocumentSetting::where('document_type', $type)
                                       ->lockForUpdate()
                                       ->first();

            if (!$settings) {
                $settings = DocumentSetting::create([
                    'document_type' => $type,
                    'prefix'        => $this->getDefaultPrefix($type),
                    'start_number'  => 1,
                    'pricing_type'  => 'exclusive'
                ]);
            }

            $nextId = $settings->start_number;

            if ($increment) {
                $settings->start_number = $nextId + 1;
                $settings->save();
            }

            return $settings->prefix . str_pad($nextId, 6, '0', STR_PAD_LEFT);
        });
    }

    public function store(Request $request, $type)
    {
        $request->validate([
            'customer_account_id' => 'required|exists:customer_accounts,id',
            'document_date'       => 'required|date',
            'lines'               => 'required|array|min:1',
        ]);

        if ($type === 'invoice') {
            $request->validate([
                'due_date' => 'required|date',
            ]);
        }

        $documentNumber = $this->generateDocumentNumber($type, true);

        $headerData = [
            'type'                    => $type,
            'document_number'         => $documentNumber,
            'customer_account_id'     => $request->customer_account_id,
            'contact_person'          => $request->contact_person,
            'document_date'           => $request->document_date,
            'delivery_date'           => $request->delivery_date,
            'order_number'            => $request->order_number,
            'project_id'              => $request->project_id,
            'representative_id'       => auth()->id(),
            'settlement_term'         => $request->settlement_term,
            'currency_code'           => $request->currency_code,
            'allow_negative'          => $request->has('allow_negative') ? 1 : 0,
            'sales_representative_id' => $request->sales_representative_id,
            'status'                  => 'draft',
            'exclusive_total'         => 0,
            'tax_amount'              => 0,
            'discount_total'          => 0,
            'inclusive_total'         => 0,
            'pricing_type'            => $request->pricing_type,
            'notes'                   => $request->special_instructions,
            'addedby' => Auth::id(),
        ];

        if ($type === 'invoice') {
            $headerData['due_date'] = $request->due_date;
        }

        $header = DocumentHeader::create($headerData);

        $exclTotal     = 0;
        $taxTotal      = 0;
        $discountTotal = 0;

        foreach ($request->lines as $line) {
            $taxRate      = Tax::find($line['tax_id'])->rate;
            $price        = $line['price'];
            $confirmedQty = $line['confirmed_quantity'] ?? $line['quantity'];

            if ($request->pricing_type === 'inclusive') {
                $priceExcl = $price / (1 + ($taxRate / 100));
                $unitTax   = $price - $priceExcl;
            } else {
                $priceExcl = $price;
                $unitTax   = 0;
            }

            $excl     = $confirmedQty * $priceExcl;
            $discount = $excl * ($line['discount_percent'] / 100);
            $taxable  = $excl - $discount;
            $tax      = $taxable * ($taxRate / 100);
            $total    = $taxable + $tax;

            $header->lines()->create([
                'module'             => $line['module'],
                'hs_code'            => $line['hs_code'] ?? null,
                'item_id'            => $line['item_id'] ?? null,
                'account_id'         => $line['account_id'] ?? null,
                'warehouse_id'       => $line['warehouse_id'] ?? null,
                'quantity'           => $line['quantity'],
                'confirmed_quantity' => $confirmedQty,
                'uom_id'             => $line['uom_id'] ?? null,
                'price'              => $price,
                'tax_id'             => $line['tax_id'],
                'tax_rate'           => $taxRate,
                'discount_percent'   => $line['discount_percent'] ?? 0,
                'line_tax'           => $request->pricing_type === 'inclusive'
                                        ? ($confirmedQty * $unitTax)
                                        : $tax,
                'line_total'         => $total,
                'description'        => $line['description'] ?? null,
            ]);

            if (!empty($line['item_id']) && !empty($line['warehouse_id'])) {
                $adjustment = 0;

                if ($type === 'invoice' || $type === 'debit_note') {
                    $adjustment = -$confirmedQty;
                } elseif ($type === 'credit_note') {
                    $adjustment = $confirmedQty;
                }

                if ($adjustment !== 0) {
                    ItemWarehouse::updateOrCreate(
                        [
                            'item_id'      => $line['item_id'],
                            'warehouse_id' => $line['warehouse_id'],
                        ],
                        [
                            'quantity' => \DB::raw('quantity + ' . $adjustment),
                        ]
                    );
                }
            }

            $exclTotal     += $taxable;
            $taxTotal      += $tax;
            $discountTotal += $discount;
        }

        $header->update([
            'exclusive_total' => $exclTotal,
            'tax_amount'      => $taxTotal,
            'discount_total'  => $discountTotal,
            'inclusive_total' => $exclTotal + $taxTotal,
            'status'          => 'processed',
        ]);

        // Handle document processing based on type
        if ($type === 'invoice') {
            return redirect()
                ->route('documents.create', ['type' => $type])
                ->with('processed_document_id', $header->id)
                ->with('success', 'Invoice processed successfully!')
                ->with('clear_form', true);
        } else {
            // For quotation, sales_order, etc. - redirect to print
            return redirect()->route('documents.print', $header->id)
                ->with('success', ucfirst($type) . ' processed successfully!');
        }
    }

    public function printDocument($id)
    {
        $document = DocumentHeader::with([
            'customer', 
            'lines.item', 
            'lines.account', 
            'lines.uom',
            'currency'
        ])->findOrFail($id);

        $company = CompanySettings::first();

        // For PDF download
        if (request()->query('download') === 'yes') {
            $pdf = PDF::loadView('documents.print-view', compact('document', 'company'));
            $filename = strtolower(str_replace(' ', '_', $document->type)) . '_' . $document->document_number . '.pdf';
            return $pdf->download($filename);
        }

        // Normal view - will trigger print
        return view('documents.print-view', compact('document', 'company'));
    }

    public function deliveryNote(DocumentHeader $document)
    {
        $pdf = PDF::loadView('documents.delivery-note', [
            'document' => $document,
            'company'  => CompanySettings::first(),
        ]);

        return $pdf->download("DN-{$document->document_number}.pdf");
    }

    public function invoice(DocumentHeader $document)
    {
        return $this->processInvoice($document);
    }

    public function processInvoice(DocumentHeader $document)
    {
        $pdf = PDF::loadView('documents.pdf', [
            'document'    => $document,
            'company'     => CompanySettings::first(),
            'pricingType' => $document->pricing_type,
        ]);

        return $pdf->download("{$document->document_number}.pdf");
    }

    public function deliveryNotePreview(DocumentHeader $document)
    {
        return view('documents.delivery-note', [
            'document' => $document,
            'company'  => CompanySettings::first(),
            'preview'  => true,
        ]);
    }

    public function invoicePreview(DocumentHeader $document)
    {
        return view('documents.pdf', [
            'document'    => $document,
            'company'     => CompanySettings::first(),
            'pricingType' => $document->pricing_type,
            'preview'     => true,
        ]);
    }

    public function pdf(DocumentHeader $document)
    {
        return view('documents.pdf', [
            'document'    => $document,
            'company'     => CompanySettings::first(),
            'pricingType' => $document->pricing_type,
        ]);
    }

    public function updateDocumentSettings(Request $request)
    {
        $request->validate([
            'prefix'        => 'nullable|string|max:10',
            'start_number'  => 'nullable|integer|min:0',
            'pricing_type'  => 'nullable|in:exclusive,inclusive',
            'document_type' => 'required|string',
        ]);

        $settings = DocumentSetting::updateOrCreate(
            ['document_type' => $request->document_type],
            array_filter([
                'prefix'       => $request->prefix,
                'start_number' => $request->start_number,
                'pricing_type' => $request->pricing_type,
            ])
        );

        if ($request->pricing_type) {
            session(['pricing_type' => $request->pricing_type]);
        }

        return response()->json(['success' => true]);
    }

    public function createReceipt()
    {
        $customers = CustomerAccount::all();
        $accounts = GeneralLedger::all();
        $receiptNumber = $this->generateReceiptNumber(false);

        return view('maintanance.process.create_receipt', [
            'customers' => $customers,
            'accounts' => $accounts,
            'receiptNumber' => $receiptNumber,
        ]);
    }

    public function storeReceipt(Request $request)
    {
        $request->validate([
            'customer_id' => 'required|exists:customer_accounts,id',
            'receipt_date' => 'required|date',
            'payment_method' => 'required|string',
            'lines' => 'required|array|min:1',
            'lines.*.description' => 'required|string',
            'lines.*.amount' => 'required|numeric|min:0.01',
            'lines.*.account_id' => 'required|exists:general_ledgers,id',
        ]);

        DB::beginTransaction();

        try {
            $receiptNumber = $this->generateReceiptNumber(true);

            $receipt = Receipt::create([
                'receipt_number' => $receiptNumber,
                'customer_id' => $request->customer_id,
                'receipt_date' => $request->receipt_date,
                'payment_method' => $request->payment_method,
                'total_amount' => array_sum(array_column($request->lines, 'amount')),
            ]);

            foreach ($request->lines as $line) {
                $receiptLine = $receipt->lines()->create([
                    'description' => $line['description'],
                    'amount' => $line['amount'],
                    'account_id' => $line['account_id'],
                ]);

                if (!empty($line['invoice_id'])) {
                    $receiptLine->update(['invoice_id' => $line['invoice_id']]);

                    $invoice = DocumentHeader::find($line['invoice_id']);
                    if ($invoice) {
                        $paidAmount = ReceiptLine::where('invoice_id', $invoice->id)->sum('amount');
                        $balance = $invoice->inclusive_total - $paidAmount;
                        $invoice->update([
                            'status' => $balance <= 0 ? 'paid' : 'partially_paid'
                        ]);
                    }
                }
            }

            DB::commit();

            return redirect()
                ->route('receipts.create')
                ->with('printed_receipt_id', $receipt->id)
                ->with('success', 'Receipt created successfully!');

        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()
                ->back()
                ->withInput()
                ->with('error', 'Failed to save receipt: ' . $e->getMessage());
        }
    }

    public function receiptPdf(Receipt $receipt)
    {
        $company = CompanySettings::first();
        $pdf = PDF::loadView('documents.receipt_pdf', [
            'receipt' => $receipt,
            'company' => $company
        ]);

        return $pdf->download("receipt-{$receipt->receipt_number}.pdf");
    }

    private function generateReceiptNumber(bool $increment = true): string
    {
        return DB::transaction(function () use ($increment) {
            $settings = DocumentSetting::firstOrCreate(
                ['document_type' => 'receipt'],
                ['prefix' => 'RC', 'start_number' => 1]
            );

            if ($increment) {
                $settings->lockForUpdate();
            }

            $number = $settings->start_number;

            if ($increment) {
                $settings->increment('start_number');
            }

            return $settings->prefix . str_pad($number, 6, '0', STR_PAD_LEFT);
        });
    }

    public function customerInvoices(CustomerAccount $customer)
    {
        return $customer->documentHeaders()
            ->where('type', 'invoice')
            ->with(['lines'])
            ->get()
            ->map(function ($invoice) {
                $paid = ReceiptLine::where('invoice_id', $invoice->id)->sum('amount');
                $balance = $invoice->inclusive_total - $paid;

                return [
                    'id' => $invoice->id,
                    'document_number' => $invoice->document_number,
                    'description' => $invoice->lines->first()->description ?? 'Invoice Payment',
                    'balance_due' => $balance,
                ];
            })
            ->filter(fn($inv) => $inv['balance_due'] > 0)
            ->values();
    }
}