<?php

namespace App\Http\Controllers;

use App\Models\Customer;
use App\Models\OperationsOrder;
use App\Models\Service;
use App\Models\ServiceSubscription;
use App\Models\ServiceSubscriptionAttachment;
use App\Models\SubscriptionPayment;
use App\Models\SubscriptionRenewal;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class CustomersController extends Controller
{
    public function __construct()
    {
        $this->middleware('permission:customers.view')->only(['index', 'show']);
        $this->middleware('permission:customers.create')->only(['store']);
        $this->middleware('permission:customers.subscriptions.create')->only(['storeSubscription']);
        $this->middleware('permission:customers.view')->only(['subscriptionAttachmentsIndex']);
        $this->middleware('permission:customers.subscriptions.update')->only(['uploadSubscriptionAttachment', 'subscriptionAttachmentsStore', 'subscriptionAttachmentsDestroy']);
        $this->middleware('permission:customers.renewals.create|renewals.promotion.create|renewals.software.create|renewals.view|renewals.promotion.view|renewals.software.view')->only(['renew']);
        $this->middleware('permission:customers.renewals.view|renewals.view')->only(['printRenewal']);
        $this->middleware('permission:pos.services.update|services.web_development.update|services.app_development.update|services.wordpress_templates.update|services.domains_emails.update|services.ads_creation.update')->only(['storePayment']);
    }

    public function index(Request $request)
    {
        // Fetch Customers with their latest subscription (if any)
        $query = Customer::with(['serviceSubscriptions.service']);

        $parts = $this->smartSearchParts($request->query('search'));
        if ($parts['search'] !== '') {
            $search = $parts['search'];
            $searchRaw = $parts['searchRaw'];
            $searchDigits = $parts['searchDigits'];
            $terms = $parts['terms'];

            $query->where(function ($q) use ($search, $searchRaw, $searchDigits, $terms) {
                $q->where(function ($textQ) use ($terms) {
                    $textQ->where(function ($nameQ) use ($terms) {
                        foreach ($terms as $t) {
                            $nameQ->where('name', 'like', '%'.$t.'%');
                        }
                    })->orWhere(function ($bizQ) use ($terms) {
                        foreach ($terms as $t) {
                            $bizQ->where('business_name', 'like', '%'.$t.'%');
                        }
                    });
                })->orWhere('phone', 'like', '%'.$searchRaw.'%')
                    ->orWhere('whatsapp', 'like', '%'.$searchRaw.'%')
                    ->orWhereHas('serviceSubscriptions', function ($subQ) use ($search) {
                        $subQ->where('invoice_number', 'like', '%'.$search.'%');
                    });

                if ($searchDigits !== '') {
                    $q->orWhere('phone', 'like', '%'.$searchDigits.'%')
                        ->orWhere('whatsapp', 'like', '%'.$searchDigits.'%');
                }
            });
        }

        $customers = $query->latest()->paginate(15)->appends($request->all());

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

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'phone' => 'required|string|max:255|unique:customers,phone',
            'whatsapp' => 'nullable|string|max:255',
            'address' => 'nullable|string',
            'business_name' => 'nullable|string|max:255',
            'business_link' => 'nullable|url',
        ]);

        Customer::create([
            'name' => $request->name,
            'phone' => $request->phone,
            'whatsapp' => $request->whatsapp,
            'address' => $request->address,
            'business_name' => $request->business_name,
            'business_link' => $request->business_link,
            'created_by' => Auth::id(),
        ]);

        return redirect()->back()->with('success', __('Customer added successfully'));
    }

    public function uploadSubscriptionAttachment(Request $request, ServiceSubscription $subscription)
    {
        $data = $request->validate([
            'name' => ['nullable', 'string', 'max:255'],
            'attachment' => ['required', 'file', 'max:20480', 'mimes:pdf,doc,docx,xls,xlsx,ppt,pptx,txt,rtf,csv,jpg,jpeg,png,webp,gif,heic,heif'],
        ]);

        $file = $data['attachment'];
        $originalName = (string) $file->getClientOriginalName();
        $mime = (string) ($file->getClientMimeType() ?? '');
        $ext = (string) ($file->getClientOriginalExtension() ?: 'bin');

        $filename = (string) Str::uuid().'.'.$ext;
        $directory = 'subscriptions/attachments/'.(string) $subscription->id;
        $path = $file->storeAs($directory, $filename, 'public');

        $displayName = trim((string) ($data['name'] ?? ''));
        if ($displayName === '') {
            $displayName = $originalName;
        }

        $attachment = ServiceSubscriptionAttachment::query()->create([
            'service_subscription_id' => $subscription->id,
            'display_name' => $displayName,
            'file_path' => $path,
            'original_name' => $originalName,
            'mime' => $mime,
            'created_by' => Auth::id(),
        ]);

        $payload = [
            'attachment_id' => $attachment->id,
            'attachment_url' => $attachment->url,
            'attachment_name' => $attachment->display_name,
        ];

        if ($request->expectsJson()) {
            return response()->json($payload);
        }

        return back()->with('success', __('common.saved_successfully'));
    }

    public function subscriptionAttachmentsIndex(ServiceSubscription $subscription)
    {
        $subscription->load([
            'customer',
            'service',
            'attachments' => function ($q) {
                $q->latest();
            },
        ]);

        $customer = $subscription->customer;
        if (! $customer) {
            abort(404);
        }

        $user = Auth::user();
        $canManage = $user instanceof User && $user->hasPermission('customers.subscriptions.update');

        return view('customers.subscription_attachments', compact('subscription', 'customer', 'canManage'));
    }

    public function subscriptionAttachmentsStore(Request $request, ServiceSubscription $subscription)
    {
        $request->merge(['name' => $request->input('display_name')]);

        return $this->uploadSubscriptionAttachment($request, $subscription);
    }

    public function subscriptionAttachmentsDestroy(ServiceSubscriptionAttachment $attachment)
    {
        $subscription = ServiceSubscription::query()->find($attachment->service_subscription_id);

        $path = (string) ($attachment->file_path ?? '');
        if ($path !== '') {
            Storage::disk('public')->delete($path);
        }

        $attachment->delete();

        if (! $subscription) {
            return back()->with('success', __('common.deleted_successfully'));
        }

        return redirect()->route('subscriptions.attachments.index', $subscription)->with('success', __('common.deleted_successfully'));
    }

    public function show($id)
    {
        // Find Customer, not subscription
        $customer = Customer::with(['serviceSubscriptions.service', 'serviceSubscriptions.renewals.creator', 'serviceSubscriptions.items', 'serviceSubscriptions.payments'])
            ->findOrFail($id);

        // Get all services for the "Add Service" dropdown
        $services = Service::all();

        return view('customers.show', compact('customer', 'services'));
    }

    public function storeSubscription(Request $request, $customerId)
    {
        $request->validate([
            'service_id' => 'required|exists:services,id',
            'details' => 'nullable|string',
            'renewal_date' => 'required|date',
            'renewal_amount' => 'required|numeric|min:0',
        ]);

        $customer = Customer::findOrFail($customerId);

        // Generate Invoice Number
        $invoiceNumber = ServiceSubscription::generateNextInvoiceNumber();

        $subscription = ServiceSubscription::create([
            'customer_id' => $customer->id,
            'service_id' => $request->service_id,
            'invoice_number' => $invoiceNumber,
            'details' => $request->details,
            'renewal_due_date' => $request->renewal_date,
            'renewal_amount' => $request->renewal_amount,
            'price' => 0, // Initial price 0 as it's an import/legacy
            'payment_method' => null, // No payment method for import
            'created_by' => Auth::id(),
        ]);

        return redirect()->back()->with('success', __('Service added successfully'));
    }

    // Keep renew method working with Subscription ID, not Customer ID
    // Route for renewal should still pass subscription ID if possible
    // OR we change it to accept subscription_id
    public function renew(Request $request, $id)
    {
        // $id here is Subscription ID based on route definition?
        // Route::post('/customers/{id}/renew', [CustomersController::class, 'renew'])->name('customers.renew');
        // This route is likely called from the modal.
        // If we change 'show' to display customer, the modal inside it should target a specific subscription.

        $paymentMethods = [
            'Cash',
            'Zain Cash',
            'POS',
            'Qi',
            'Switch',
        ];

        $request->validate([
            'renewal_date' => 'required|date',
            'amount' => 'required|numeric|min:0',
            'payment_method' => 'required|string|in:'.implode(',', $paymentMethods),
            'note' => 'nullable|string',
        ]);

        $subscription = ServiceSubscription::with('service')->findOrFail($id);
        $serviceType = (string) ($subscription->service?->type ?? '');

        DB::beginTransaction();
        try {
            $requestedRenewalDate = Carbon::parse($request->renewal_date)->startOfDay();
            $isMonthlySubscription = in_array($serviceType, ['monthly_subscription_open', 'monthly_subscription_limited'], true);
            $shouldResetOperationsCycle = in_array($serviceType, ['monthly_subscription_open', 'monthly_subscription_limited', 'one_time_service'], true);
            $effectiveRenewalDate = $isMonthlySubscription ? now()->startOfDay()->addDays(30) : $requestedRenewalDate;

            // Generate Invoice Number
            $invoiceNumber = ServiceSubscription::generateNextInvoiceNumber();

            // Create Renewal Record
            $renewalPayload = [
                'service_subscription_id' => $subscription->id,
                'invoice_number' => $invoiceNumber,
                'renewal_date' => $effectiveRenewalDate->toDateString(),
                'amount' => $request->amount,
                'note' => $request->note,
                'created_by' => Auth::id(),
            ];
            if (Schema::hasColumn('subscription_renewals', 'payment_method')) {
                $renewalPayload['payment_method'] = $request->payment_method;
            }
            SubscriptionRenewal::create($renewalPayload);

            SubscriptionPayment::create([
                'service_subscription_id' => $subscription->id,
                'amount' => (int) $request->amount,
                'payment_method' => $request->payment_method,
                'payment_date' => now()->toDateString(),
                'notes' => $request->note,
                'created_by' => Auth::id(),
            ]);

            // Update Subscription Renewal Due Date (User likely enters the NEW due date or the date of renewal?)
            // Usually renewal_date in form is "When is the next renewal due?"
            // Or is it "When was this renewed?"
            // Given the prompt: "Renewal... save... adds to bar with renewal date".
            // And usually a renewal extends the subscription.
            // Let's assume the input `renewal_date` is the NEW due date.
            // So we update `renewal_due_date` on subscription.

            $subscriptionUpdatePayload = [
                'renewal_due_date' => $effectiveRenewalDate->toDateString(),
            ];
            if ($shouldResetOperationsCycle) {
                $subscriptionUpdatePayload['invoice_number'] = $invoiceNumber;
                $subscriptionUpdatePayload['payment_method'] = $request->payment_method;
                $subscriptionUpdatePayload['work_employee_id'] = null;
            }
            $subscription->update($subscriptionUpdatePayload);

            if ($shouldResetOperationsCycle) {
                OperationsOrder::create([
                    'service_subscription_id' => $subscription->id,
                    'customer_name' => $subscription->customer_name,
                    'phone' => $subscription->phone,
                    'created_by' => Auth::id(),
                ]);
            }

            DB::commit();

            return redirect()->back()->with('success', __('Renewal processed successfully'));

        } catch (QueryException $e) {
            DB::rollBack();

            $message = $e->getMessage();
            if (str_contains($message, 'Unknown column \'payment_method\'') || str_contains($message, 'Unknown column `payment_method`')) {
                return redirect()->back()->with('error', 'قاعدة البيانات تحتاج تحديث (Migration). شغّل الأمر: php artisan migrate');
            }

            return redirect()->back()->with('error', 'Error processing renewal: '.$message);
        } catch (\Exception $e) {
            DB::rollBack();

            return redirect()->back()->with('error', 'Error processing renewal: '.$e->getMessage());
        }
    }

    public function storePayment(Request $request, ServiceSubscription $subscription)
    {
        $paymentMethods = [
            'Cash',
            'Zain Cash',
            'POS',
            'Qi',
            'Switch',
        ];

        $validated = $request->validate([
            'amount' => 'required|integer|min:1',
            'payment_method' => 'required|string|in:'.implode(',', $paymentMethods),
        ]);

        $subscription->loadMissing('payments');

        $total = (int) max(0, ((int) $subscription->price - (int) $subscription->discount));
        $paid = (int) $subscription->payments->sum('amount');
        $remaining = (int) max(0, $total - $paid);

        if ((int) $validated['amount'] > $remaining) {
            return redirect()->back()->withErrors([
                'amount' => __('accounts.debts.validation.amount_exceeds_remaining'),
            ]);
        }

        SubscriptionPayment::create([
            'service_subscription_id' => $subscription->id,
            'amount' => (int) $validated['amount'],
            'payment_method' => $validated['payment_method'],
            'payment_date' => now()->toDateString(),
            'created_by' => Auth::id(),
        ]);

        return redirect()->back()->with('success', __('accounts.debts.messages.payment_added'));
    }

    public function printRenewal($id)
    {
        app()->setLocale('en');

        $renewal = SubscriptionRenewal::with(['serviceSubscription.service', 'creator'])->findOrFail($id);

        $barcode = $this->generateBarcodeSVG($renewal->invoice_number);

        return view('customers.print_renewal', compact('renewal', 'barcode'));
    }

    private function generateBarcodeSVG($code)
    {
        // Code 39 Generator
        $code = strtoupper($code);
        $displayCode = $code;
        $code = '*'.$code.'*'; // Start and stop characters

        // B = Wide Bar, b = Narrow Bar, W = Wide Space, w = Narrow Space
        $barcodeTable = [
            '0' => 'bwbWBwBwb', '1' => 'BwbWbwbwB', '2' => 'bwBWbwbwB', '3' => 'BwBWbwbwb',
            '4' => 'bwbWBwbwB', '5' => 'BwbWBwbwb', '6' => 'bwBWBwbwb', '7' => 'bwbWbwBwB',
            '8' => 'BwbWbwBwb', '9' => 'bwBWbwBwb', 'A' => 'BwbwbWbwB', 'B' => 'bwBwbWbwB',
            'C' => 'BwBwbWbwb', 'D' => 'bwbwBWbwB', 'E' => 'BwbwBWbwb', 'F' => 'bwBwBWbwb',
            'G' => 'bwbwbWBwB', 'H' => 'BwbwbWBwb', 'I' => 'bwBwbWBwb', 'J' => 'bwbwBWBwb',
            'K' => 'BwbwbwbWB', 'L' => 'bwBwbwbWB', 'M' => 'BwBwbwbWb', 'N' => 'bwbwBwbWB',
            'O' => 'BwbwBwbWb', 'P' => 'bwBwBwbWb', 'Q' => 'bwbwbwBWB', 'R' => 'BwbwbwBWb',
            'S' => 'bwBwbwBWb', 'T' => 'bwbwBwBWb', 'U' => 'BWbwbwbwB', 'V' => 'bWBwbwbwB',
            'W' => 'BWBwbwbwb', 'X' => 'bWbwBwbwB', 'Y' => 'BWbwBwbwb', 'Z' => 'bWBwBwbwb',
            '-' => 'bWbwbwBwB', '.' => 'BWbwbwBwb', ' ' => 'bWBwbwBwb', '*' => 'bWbwBwBwb',
            '$' => 'bWbWbWbwb', '/' => 'bWbWbwbWb', '+' => 'bWbwbWbWb', '%' => 'bwbWbWbWb',
        ];

        $svg = '';
        $currentX = 10;
        $narrow = 1.5;
        $wide = 4;
        $gap = 1.5;

        for ($i = 0; $i < strlen($code); $i++) {
            $char = $code[$i];
            if (! isset($barcodeTable[$char])) {
                continue;
            }

            $pattern = $barcodeTable[$char];
            for ($j = 0; $j < 9; $j++) {
                $type = $pattern[$j];
                $isBar = ($j % 2 == 0); // Even index is bar, odd is space
                $width = (strtoupper($type) === $type) ? $wide : $narrow;

                if ($isBar) {
                    $svg .= '<rect x="'.$currentX.'" y="5" width="'.$width.'" height="40" fill="black"/>';
                }

                $currentX += $width;
            }
            $currentX += $gap;
        }

        $totalWidth = $currentX + 10;
        $height = 50;

        return '<svg xmlns="http://www.w3.org/2000/svg" width="'.$totalWidth.'" height="'.$height.'" viewBox="0 0 '.$totalWidth.' '.$height.'"><rect x="0" y="0" width="'.$totalWidth.'" height="'.$height.'" fill="white"/>'.$svg.'</svg>';
    }
}
