<?php

namespace App\Http\Controllers;

use App\Models\Sale;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;

class SaleController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth']);
    }

    /* =========================
     |  LIST + FILTERS
     * =========================*/
    public function index(Request $request): View
    {
        $q = Sale::query()
            ->when($request->filled('company_id'), fn($qr) =>
                $qr->where('company_id', (int)$request->company_id)
            )
            ->when($request->filled('customer_id'), fn($qr) =>
                $qr->where('customer_id', (int)$request->customer_id)
            )
            ->when($request->filled('date_from'), fn($qr) =>
                $qr->whereDate('date', '>=', $request->date_from)
            )
            ->when($request->filled('date_to'), fn($qr) =>
                $qr->whereDate('date', '<=', $request->date_to)
            )
            ->latest('date')->latest('id');

        $sales = $q->paginate(20)->withQueryString();

        [$companies, $customers] = $this->safeLookupLists();

        return view('sales.index', compact('sales', 'companies', 'customers'));
    }

    /* =========================
     |  CREATE FORM
     * =========================*/
    public function create(): View
    {
        [$companies, $customers, $employees, $warehouses, $products, $banks] =
            $this->safeLookupLists(true);

        return view('sales.create', compact(
            'companies','customers','employees','warehouses','products','banks'
        ));
    }

    /* =========================
     |  STORE
     * =========================*/
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'company_id'  => ['required','integer','min:1'],
            'customer_id' => ['required','integer','min:1'],
            'employee_id' => ['nullable','integer','min:1'],
            'date'        => ['required','date'],
            'reference'   => ['nullable','string','max:191'],
            'discount'    => ['nullable','numeric','min:0'],
        ]);

        $items = $request->input('items', []);
        if (!is_array($items) || count($items) === 0) {
            return back()->withErrors(['items' => 'Please add at least one item.'])->withInput();
        }

        foreach ($items as $i => $line) {
            $request->validate([
                "items.$i.product_id"   => ['required','integer','min:1'],
                "items.$i.warehouse_id" => ['nullable','integer','min:1'],
                "items.$i.qty"          => ['required','numeric','min:0.0001'],
                "items.$i.adjust_kg"    => ['nullable','numeric','min:0'],
                "items.$i.price"        => ['required','numeric','min:0'],
            ]);
        }

        $subTotal = 0.0;
        foreach ($items as $line) {
            $qty     = (float)($line['qty'] ?? 0);
            $adjKg   = (float)($line['adjust_kg'] ?? 0);
            $price   = (float)($line['price'] ?? 0);
            $totalKg = $qty + $adjKg;
            $subTotal += $totalKg * $price;
        }
        $discount   = (float)($request->discount ?? 0);
        $grandTotal = max($subTotal - $discount, 0);
        $userId     = auth()->id();

        DB::transaction(function () use ($request, $items, $subTotal, $discount, $grandTotal, $userId) {
            $payload = [];
            $this->putIfColumn($payload, 'company_id',   (int)$request->company_id,     'rm_sales');
            $this->putIfColumn($payload, 'customer_id',  (int)$request->customer_id,    'rm_sales');
            $this->putIfColumn($payload, 'employee_id',  $request->filled('employee_id') ? (int)$request->employee_id : null, 'rm_sales');
            $this->putIfColumn($payload, 'date',         $request->date,                 'rm_sales');
            $this->putIfColumn($payload, 'reference',    (string)($request->reference ?? ''), 'rm_sales');
            $this->putIfColumn($payload, 'sub_total',    round($subTotal, 2),            'rm_sales');
            $this->putIfColumn($payload, 'discount',     round($discount, 2),            'rm_sales');
            $this->putIfColumn($payload, 'grand_total',  round($grandTotal, 2),          'rm_sales');
            $this->putIfColumn($payload, 'due',          round($grandTotal, 2),          'rm_sales');
            $this->putIfColumn($payload, 'created_by',   $userId,                         'rm_sales');
            $this->putIfColumn($payload, 'updated_by',   $userId,                         'rm_sales');

            /** @var Sale $sale */
            $sale = Sale::query()->create($payload);

            if (Schema::hasTable('rm_sale_items')) {
                foreach ($items as $line) {
                    $qty     = (float)($line['qty'] ?? 0);
                    $adjKg   = (float)($line['adjust_kg'] ?? 0);
                    $price   = (float)($line['price'] ?? 0);
                    $totalKg = $qty + $adjKg;
                    $sub     = $totalKg * $price;

                    $row = [
                        'sale_id'      => $sale->id,
                        'product_id'   => (int)($line['product_id'] ?? 0),
                        'warehouse_id' => !empty($line['warehouse_id']) ? (int)$line['warehouse_id'] : null,
                        'qty'          => round($qty, 3),
                        'adjust_kg'    => round($adjKg, 3),
                        'total_kg'     => round($totalKg, 3),
                        'price'        => round($price, 2),
                        'subtotal'     => round($sub, 2),
                        'unit'         => $line['unit']     ?? null,
                        'category'     => $line['category'] ?? null,
                    ];

                    $filtered = [];
                    foreach ($row as $col => $val) {
                        if (Schema::hasColumn('rm_sale_items', $col)) {
                            $filtered[$col] = $val;
                        }
                    }

                    DB::table('rm_sale_items')->insert($filtered);
                }
            } elseif (Schema::hasColumn('rm_sales', 'lines_json')) {
                DB::table('rm_sales')->where('id', $sale->id)->update([
                    'lines_json' => json_encode($items),
                ]);
            }
        });

        return redirect()->route('sales.index')->with('success', 'Sale created successfully.');
    }

    /* =========================
     |  SHOW
     * =========================*/
    public function show(Sale $sale): View
    {
        $items = Schema::hasTable('rm_sale_items')
            ? DB::table('rm_sale_items')->where('sale_id', $sale->id)->get()
            : collect();

        return view('sales.show', compact('sale', 'items'));
    }

    /* =========================
     |  EDIT
     * =========================*/
    public function edit(Sale $sale): View
    {
        [$companies, $customers, $employees, $warehouses, $products, $banks] =
            $this->safeLookupLists(true);

        $items = Schema::hasTable('rm_sale_items')
            ? DB::table('rm_sale_items')->where('sale_id', $sale->id)->get()
            : collect();

        return view('sales.edit', compact(
            'sale','items','companies','customers','employees','warehouses','products','banks'
        ));
    }

    /* =========================
     |  UPDATE
     * =========================*/
    public function update(Request $request, Sale $sale): RedirectResponse
    {
        $request->validate([
            'company_id'  => ['required','integer','min:1'],
            'customer_id' => ['required','integer','min:1'],
            'employee_id' => ['nullable','integer','min:1'],
            'date'        => ['required','date'],
            'reference'   => ['nullable','string','max:191'],
            'discount'    => ['nullable','numeric','min:0'],
        ]);

        $items = $request->input('items', []);
        if (!is_array($items) || count($items) === 0) {
            return back()->withErrors(['items' => 'Please add at least one item.'])->withInput();
        }
        foreach ($items as $i => $line) {
            $request->validate([
                "items.$i.product_id"   => ['required','integer','min:1'],
                "items.$i.warehouse_id" => ['nullable','integer','min:1'],
                "items.$i.qty"          => ['required','numeric','min:0.0001'],
                "items.$i.adjust_kg"    => ['nullable','numeric','min:0'],
                "items.$i.price"        => ['required','numeric','min:0'],
            ]);
        }

        $subTotal = 0.0;
        foreach ($items as $line) {
            $qty     = (float)($line['qty'] ?? 0);
            $adjKg   = (float)($line['adjust_kg'] ?? 0);
            $price   = (float)($line['price'] ?? 0);
            $totalKg = $qty + $adjKg;
            $subTotal += $totalKg * $price;
        }
        $discount   = (float)($request->discount ?? 0);
        $grandTotal = max($subTotal - $discount, 0);
        $userId     = auth()->id();

        DB::transaction(function () use ($request, $sale, $items, $subTotal, $discount, $grandTotal, $userId) {
            $payload = [];
            $this->putIfColumn($payload, 'company_id',   (int)$request->company_id,     'rm_sales');
            $this->putIfColumn($payload, 'customer_id',  (int)$request->customer_id,    'rm_sales');
            $this->putIfColumn($payload, 'employee_id',  $request->filled('employee_id') ? (int)$request->employee_id : null, 'rm_sales');
            $this->putIfColumn($payload, 'date',         $request->date,                 'rm_sales');
            $this->putIfColumn($payload, 'reference',    (string)($request->reference ?? ''), 'rm_sales');
            $this->putIfColumn($payload, 'sub_total',    round($subTotal, 2),            'rm_sales');
            $this->putIfColumn($payload, 'discount',     round($discount, 2),            'rm_sales');
            $this->putIfColumn($payload, 'grand_total',  round($grandTotal, 2),          'rm_sales');
            $this->putIfColumn($payload, 'due',          round($grandTotal, 2),          'rm_sales');
            $this->putIfColumn($payload, 'updated_by',   $userId,                         'rm_sales');

            $sale->update($payload);

            if (Schema::hasTable('rm_sale_items')) {
                DB::table('rm_sale_items')->where('sale_id', $sale->id)->delete();

                foreach ($items as $line) {
                    $qty     = (float)($line['qty'] ?? 0);
                    $adjKg   = (float)($line['adjust_kg'] ?? 0);
                    $price   = (float)($line['price'] ?? 0);
                    $totalKg = $qty + $adjKg;
                    $sub     = $totalKg * $price;

                    $row = [
                        'sale_id'      => $sale->id,
                        'product_id'   => (int)($line['product_id'] ?? 0),
                        'warehouse_id' => !empty($line['warehouse_id']) ? (int)$line['warehouse_id'] : null,
                        'qty'          => round($qty, 3),
                        'adjust_kg'    => round($adjKg, 3),
                        'total_kg'     => round($totalKg, 3),
                        'price'        => round($price, 2),
                        'subtotal'     => round($sub, 2),
                        'unit'         => $line['unit']     ?? null,
                        'category'     => $line['category'] ?? null,
                    ];

                    $filtered = [];
                    foreach ($row as $col => $val) {
                        if (Schema::hasColumn('rm_sale_items', $col)) {
                            $filtered[$col] = $val;
                        }
                    }
                    DB::table('rm_sale_items')->insert($filtered);
                }
            } elseif (Schema::hasColumn('rm_sales', 'lines_json')) {
                DB::table('rm_sales')->where('id', $sale->id)->update([
                    'lines_json' => json_encode($items),
                ]);
            }
        });

        return redirect()->route('sales.index')->with('success','Sale updated successfully.');
    }

    /* =========================
     |  DESTROY
     * =========================*/
    public function destroy(Sale $sale): RedirectResponse
    {
        DB::transaction(function () use ($sale) {
            if (Schema::hasTable('rm_sale_items')) {
                DB::table('rm_sale_items')->where('sale_id', $sale->id)->delete();
            }
            $sale->delete();
        });

        return redirect()->route('sales.index')->with('success','Sale deleted.');
    }

    /* ========================================================
     | Helpers
     * ========================================================*/

    private function safeLookupLists(bool $full = false): array
    {
        $companies  = $this->tryList('App\\Models\\Company',   ['id','name']);
        $customers  = $this->tryList('App\\Models\\Customer',  ['id','name']);

        if (!$full) return [$companies, $customers];

        $employees  = $this->tryList('App\\Models\\Employee',  ['id','name']);
        $warehouses = $this->tryList('App\\Models\\Warehouse', ['id','name']);
        $products   = $this->tryList('App\\Models\\Product',   ['id','name','price','available_qty']);
        $banks      = $this->tryList('App\\Models\\Bank',      ['id','name']);

        return [$companies,$customers,$employees,$warehouses,$products,$banks];
    }

    private function tryList(string $class, array $cols)
    {
        if (class_exists($class)) {
            try { return $class::select($cols)->orderBy($cols[1] ?? 'id')->get(); }
            catch (\Throwable $e) { return collect(); }
        }
        return collect();
    }

    private function putIfColumn(array &$target, string $col, $value, string $table): void
    {
        if (Schema::hasColumn($table, $col)) {
            $target[$col] = $value;
        }
    }
}
