<?php

namespace App\Http\Controllers\Admin;

use App\Models\Company;
use App\Models\Customer;
use App\Models\Vendor;
use App\Services\CustomerStatementBuilder;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;

class CustomerController extends AdminController
{
    public function index(Request $request)
    {
        $companyId = $this->companyId();
        $filters = $request->only(['q', 'company_name', 'contact_name', 'email', 'phone', 'city']);
        $sortBy = $request->query('sort', 'name');
        $sortDir = $request->query('dir', 'asc') === 'desc' ? 'desc' : 'asc';
        if (! array_key_exists($sortBy, config('customer.sort_options'))) {
            $sortBy = 'name';
        }
        $clientId = $request->integer('client') ?: null;
        $view = $request->query('view', 'detail');
        $statementFrom = $request->query('from', now()->subMonth()->format('Y-m-d'));
        $statementTo = $request->query('to', now()->format('Y-m-d'));
        $txnFilter = $request->query('txn', 'all');

        $query = Customer::where('company_id', $companyId)
            ->when($filters['company_name'] ?? null, fn ($q, $v) => $q->where('name', 'like', "%{$v}%"))
            ->when($filters['contact_name'] ?? null, fn ($q, $v) => $q->where('contact_person', 'like', "%{$v}%"))
            ->when($filters['email'] ?? null, fn ($q, $v) => $q->where('email', 'like', "%{$v}%"))
            ->when($filters['phone'] ?? null, fn ($q, $v) => $q->where('phone', 'like', "%{$v}%"))
            ->when($filters['city'] ?? null, fn ($q, $v) => $q->where('city', 'like', "%{$v}%"))
            ->when($filters['q'] ?? null, function ($q, $v) {
                $q->where(function ($sub) use ($v) {
                    $sub->where('name', 'like', "%{$v}%")
                        ->orWhere('gstin', 'like', "%{$v}%")
                        ->orWhere('phone', 'like', "%{$v}%")
                        ->orWhere('email', 'like', "%{$v}%")
                        ->orWhere('contact_person', 'like', "%{$v}%")
                        ->orWhere('city', 'like', "%{$v}%");
                });
            });

        if ($sortBy === 'balance') {
            $customers = $query->get()->sortBy(
                fn ($c) => $c->currentBalance(),
                SORT_REGULAR,
                $sortDir === 'desc'
            )->values();
        } else {
            $customers = $query->orderBy($sortBy, $sortDir)->get();
        }
        $cities = Customer::where('company_id', $companyId)
            ->whereNotNull('city')->where('city', '!=', '')
            ->distinct()->orderBy('city')->pluck('city');

        $hasFilters = collect($filters)->filter()->isNotEmpty();
        $selected = $clientId ? $customers->firstWhere('id', $clientId) : null;

        if ($clientId && ! $selected) {
            $selected = Customer::where('company_id', $companyId)->find($clientId);
            if ($selected) {
                $customers = $customers->push($selected)->sortBy('name')->values();
            }
        }

        $statement = null;
        if ($selected && $view === 'statement') {
            $statement = CustomerStatementBuilder::for(
                $selected,
                $statementFrom,
                $statementTo,
                array_key_exists($txnFilter, config('customer.statement_txn_filters')) ? $txnFilter : 'all'
            )->build();
        }

        return view('admin.customers.index', [
            'customers' => $customers,
            'filters' => $filters,
            'cities' => $cities,
            'hasFilters' => $hasFilters,
            'selected' => $selected,
            'view' => $view,
            'company' => Company::find($companyId),
            'statement' => $statement,
            'statementFrom' => $statementFrom,
            'statementTo' => $statementTo,
            'txnFilter' => $txnFilter,
            'txnFilters' => config('customer.statement_txn_filters'),
            'panelOpen' => (bool) $clientId,
            'sortBy' => $sortBy,
            'sortDir' => $sortDir,
            'sortOptions' => config('customer.sort_options'),
            'sortOptionsTable' => config('customer.sort_options_table'),
        ]);
    }

    public function export(Request $request): StreamedResponse
    {
        $companyId = $this->companyId();
        $customers = Customer::where('company_id', $companyId)->orderBy('name')->get();

        $filename = 'clients-'.date('Y-m-d').'.csv';

        return response()->streamDownload(function () use ($customers) {
            $out = fopen('php://output', 'w');
            fputcsv($out, ['Company', 'Contact', 'Balance', 'Billing Address', 'City', 'Email', 'Phone', 'Notes']);
            foreach ($customers as $c) {
                fputcsv($out, [
                    $c->name,
                    $c->contact_person ?? '',
                    $c->currentBalance(),
                    $c->billingAddressShort(80),
                    $c->city ?? '',
                    $c->email ?? '',
                    $c->phone ?? '',
                    $c->notes ?? '',
                ]);
            }
            fclose($out);
        }, $filename, ['Content-Type' => 'text/csv']);
    }

    public function create()
    {
        return view('admin.customers.create', $this->formOptions());
    }

    public function quickStore(Request $request)
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'phone' => ['nullable', 'string', 'max:20'],
            'email' => ['nullable', 'email', 'max:255'],
            'gst_treatment' => ['nullable', 'string', 'max:100'],
            'gstin' => ['nullable', 'string', 'max:15'],
            'pan' => ['nullable', 'string', 'max:10'],
            'tan' => ['nullable', 'string', 'max:20'],
            'currency' => ['nullable', 'string', 'max:10'],
            'also_use_as_vendor' => ['nullable', 'boolean'],
            'client_wise_item_price' => ['nullable', 'boolean'],
            'contact_person' => ['nullable', 'string', 'max:255'],
            'contact_phone' => ['nullable', 'string', 'max:20'],
            'contact_email' => ['nullable', 'email', 'max:255'],
            'billing_address_line1' => ['nullable', 'string', 'max:255'],
            'billing_address_line2' => ['nullable', 'string', 'max:255'],
            'country' => ['nullable', 'string', 'max:100'],
            'state' => ['nullable', 'string', 'max:100'],
            'city' => ['nullable', 'string', 'max:100'],
            'pincode' => ['nullable', 'string', 'max:10'],
            'has_shipping_address' => ['nullable', 'boolean'],
            'shipping_address_line1' => ['nullable', 'string', 'max:255'],
            'shipping_address_line2' => ['nullable', 'string', 'max:255'],
            'shipping_country' => ['nullable', 'string', 'max:100'],
            'shipping_state' => ['nullable', 'string', 'max:100'],
            'shipping_city' => ['nullable', 'string', 'max:100'],
            'shipping_pincode' => ['nullable', 'string', 'max:10'],
            'additional_contacts' => ['nullable', 'array'],
            'additional_contacts.*.name' => ['nullable', 'string', 'max:255'],
            'additional_contacts.*.phone' => ['nullable', 'string', 'max:20'],
            'additional_contacts.*.email' => ['nullable', 'email', 'max:255'],
        ]);

        $data['company_id'] = $this->companyId();
        $data['opening_balance'] = 0;
        $data['balance_type'] = 'debit';
        $data['is_active'] = true;
        $data = $this->prepareCustomerData($request, $data);

        $customer = Customer::create($data);

        if ($request->boolean('also_use_as_vendor')) {
            $this->syncAsVendor($customer);
        }

        return response()->json([
            'ok' => true,
            'customer' => $customer->fresh()->toPickerArray(),
            'edit_url' => route('admin.customers.edit', $customer),
        ]);
    }

    public function store(Request $request)
    {
        $data = $this->validateCustomer($request);
        $data['company_id'] = $this->companyId();
        $data = $this->prepareCustomerData($request, $data);

        $customer = Customer::create($data);

        if ($request->boolean('also_use_as_vendor')) {
            $this->syncAsVendor($customer);
        }

        return redirect()->route('admin.customers.index', ['client' => $customer->id])
            ->with('success', 'Customer created successfully.');
    }

    public function edit(Customer $customer)
    {
        $this->authorizeCustomer($customer);

        return view('admin.customers.edit', array_merge(
            ['customer' => $customer],
            $this->formOptions()
        ));
    }

    public function update(Request $request, Customer $customer)
    {
        $this->authorizeCustomer($customer);

        $data = $this->validateCustomer($request);
        $data = $this->prepareCustomerData($request, $data);

        $customer->update($data);

        if ($request->boolean('also_use_as_vendor')) {
            $this->syncAsVendor($customer);
        }

        return redirect()->route('admin.customers.index', ['client' => $customer->id])
            ->with('success', 'Customer updated successfully.');
    }

    public function destroy(Customer $customer)
    {
        $this->authorizeCustomer($customer);
        $customer->delete();

        return redirect()->route('admin.customers.index')
            ->with('success', 'Customer deleted successfully.');
    }

    private function formOptions(): array
    {
        return [
            'gstTreatments' => config('customer.gst_treatments'),
            'currencies' => config('customer.currencies'),
            'countries' => config('customer.countries'),
            'indianStates' => config('customer.indian_states'),
        ];
    }

    private function validateCustomer(Request $request): array
    {
        return $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'phone' => ['nullable', 'string', 'max:20'],
            'email' => ['nullable', 'email', 'max:255'],
            'gst_treatment' => ['nullable', 'string', 'max:100'],
            'gstin' => ['nullable', 'string', 'max:15'],
            'pan' => ['nullable', 'string', 'max:10'],
            'tan' => ['nullable', 'string', 'max:20'],
            'tin' => ['nullable', 'string', 'max:50'],
            'vat' => ['nullable', 'string', 'max:50'],
            'website' => ['nullable', 'string', 'max:255'],
            'currency' => ['nullable', 'string', 'max:10'],
            'also_use_as_vendor' => ['nullable', 'boolean'],
            'client_wise_item_price' => ['nullable', 'boolean'],
            'contact_person' => ['nullable', 'string', 'max:255'],
            'contact_phone' => ['nullable', 'string', 'max:20'],
            'contact_email' => ['nullable', 'email', 'max:255'],
            'billing_address_line1' => ['nullable', 'string', 'max:255'],
            'billing_address_line2' => ['nullable', 'string', 'max:255'],
            'country' => ['nullable', 'string', 'max:100'],
            'state' => ['nullable', 'string', 'max:100'],
            'city' => ['nullable', 'string', 'max:100'],
            'pincode' => ['nullable', 'string', 'max:10'],
            'has_shipping_address' => ['nullable', 'boolean'],
            'shipping_address_line1' => ['nullable', 'string', 'max:255'],
            'shipping_address_line2' => ['nullable', 'string', 'max:255'],
            'shipping_country' => ['nullable', 'string', 'max:100'],
            'shipping_state' => ['nullable', 'string', 'max:100'],
            'shipping_city' => ['nullable', 'string', 'max:100'],
            'shipping_pincode' => ['nullable', 'string', 'max:10'],
            'other_info' => ['nullable', 'string'],
            'notes' => ['nullable', 'string'],
            'opening_balance' => ['required', 'numeric', 'min:0'],
            'balance_type' => ['required', 'in:debit,credit'],
            'is_active' => ['nullable', 'boolean'],
            'additional_contacts' => ['nullable', 'array'],
            'additional_contacts.*.name' => ['nullable', 'string', 'max:255'],
            'additional_contacts.*.phone' => ['nullable', 'string', 'max:20'],
            'additional_contacts.*.email' => ['nullable', 'email', 'max:255'],
        ]);
    }

    private function prepareCustomerData(Request $request, array $data): array
    {
        $data['also_use_as_vendor'] = $request->boolean('also_use_as_vendor');
        $data['client_wise_item_price'] = $request->boolean('client_wise_item_price');
        $data['has_shipping_address'] = $request->boolean('has_shipping_address');
        $data['is_active'] = $request->boolean('is_active', true);
        $data['currency'] = $data['currency'] ?? 'INR';
        $data['country'] = $data['country'] ?? 'India';
        $data['opening_balance'] = $data['opening_balance'] ?? 0;
        $data['balance_type'] = $data['balance_type'] ?? 'debit';

        $data['billing_address'] = trim(
            ($data['billing_address_line1'] ?? '')."\n".($data['billing_address_line2'] ?? '')
        );

        if ($data['has_shipping_address']) {
            $data['shipping_address'] = trim(
                ($data['shipping_address_line1'] ?? '')."\n".($data['shipping_address_line2'] ?? '')
            );
        } else {
            $data['shipping_address'] = null;
            $data['shipping_address_line1'] = null;
            $data['shipping_address_line2'] = null;
            $data['shipping_city'] = null;
            $data['shipping_state'] = null;
            $data['shipping_pincode'] = null;
            $data['shipping_country'] = null;
        }

        $contacts = collect($request->input('additional_contacts', []))
            ->filter(fn ($c) => ! empty($c['name']) || ! empty($c['phone']) || ! empty($c['email']))
            ->values()
            ->all();

        $data['additional_contacts'] = $contacts ?: null;

        return $data;
    }

    private function syncAsVendor(Customer $customer): void
    {
        Vendor::updateOrCreate(
            [
                'company_id' => $customer->company_id,
                'gstin' => $customer->gstin,
                'name' => $customer->name,
            ],
            [
                'contact_person' => $customer->contact_person,
                'email' => $customer->email,
                'phone' => $customer->phone,
                'address' => $customer->billing_address,
                'city' => $customer->city,
                'state' => $customer->state,
                'pincode' => $customer->pincode,
                'country' => $customer->country,
                'opening_balance' => $customer->opening_balance,
                'balance_type' => $customer->balance_type === 'debit' ? 'credit' : 'debit',
                'is_active' => $customer->is_active,
            ]
        );
    }

    private function authorizeCustomer(Customer $customer): void
    {
        if ($customer->company_id !== $this->companyId()) {
            abort(403);
        }
    }
}
