<?php

namespace App\Http\Controllers\Transaction;

use App\Http\Controllers\Controller;
use App\Models\Service;
use App\Models\ServiceDetail;
use App\Models\ServiceStatusHistory;
use App\Models\Product;
use App\Models\Customer;
use App\Models\Branch;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Barryvdh\DomPDF\Facade\Pdf;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use BaconQrCode\Encoder\Encoder;
use BaconQrCode\Common\ErrorCorrectionLevel;

class ServiceController extends Controller
{
    public function __construct()
    {
        $this->middleware(['permission:services.index'])->only('index', 'show');
        $this->middleware(['permission:services.create'])->only(['create', 'store']);
        $this->middleware(['permission:services.edit'])->only(['edit', 'update']);
        $this->middleware(['permission:services.delete'])->only('destroy');
    }

    /**
     * Display a listing of services
     */
    public function index()
    {
        $title = "service";
        $services = Service::with(['customer', 'branch', 'technician'])
            ->orderBy('id', 'desc')
            ->get();

        return view('transaction.service.index', compact('title', 'services'));
    }

    /**
     * Show the form for creating a new service
     */
    public function create()
    {
        $title = "service";
        $customers = Customer::orderBy('name', 'asc')->get();
        $branches = Branch::orderBy('name', 'asc')->get();

        // Get technicians - handle jika role technician belum ada
        try {
            $technicians = User::role('technician')->orderBy('name', 'asc')->get();
        } catch (\Exception $e) {
            // Jika role technician belum ada, ambil semua user
            $technicians = User::orderBy('name', 'asc')->get();
        }

        $products = Product::with(['brand', 'category', 'productStocks', 'productPrices'])->get();

        return view('transaction.service.create', compact('title', 'customers', 'branches', 'technicians', 'products'));
    }

    /**
     * Store a newly created service in storage
     */
    public function store(Request $request)
    {
        $request->validate([
            'customer_id' => 'required|exists:customers,id',
            'branch_id' => 'required|exists:branches,id',
            'service_date' => 'required|date',
            'device_type' => 'required|string',
            'device_brand' => 'required|string',
            'device_model' => 'required|string',
            'complaint' => 'required|string',
        ]);

        DB::beginTransaction();
        try {
            // Generate service number
            $lastService = Service::whereDate('created_at', today())->latest()->first();
            $number = $lastService ? (int) substr($lastService->service_number, -4) + 1 : 1;
            $serviceNumber = 'SRV-' . date('Ymd') . '-' . str_pad($number, 4, '0', STR_PAD_LEFT);

            // Generate barcode
            $barcode = $this->generateBarcode();

            // Create service
            $service = Service::create([
                'service_number' => $serviceNumber,
                'barcode' => $barcode,
                'customer_id' => $request->customer_id,
                'branch_id' => $request->branch_id,
                'technician_id' => $request->technician_id,
                'service_date' => $request->service_date,
                'device_type' => $request->device_type,
                'device_brand' => $request->device_brand,
                'device_model' => $request->device_model,
                'imei' => $request->imei,
                'complaint' => $request->complaint,
                'diagnosis' => $request->diagnosis,
                'estimated_cost' => $request->estimated_cost ?? 0,
                'service_fee' => $request->service_fee ?? 0,
                'down_payment' => $request->down_payment ?? 0,
                'priority' => $request->priority ?? 'normal',
                'estimated_completion' => $request->estimated_completion,
                'notes' => $request->notes,
                'status' => 'menunggu diagnosa',
            ]);

            $partsCost = 0;
            // Save service details (spare parts) if any
            if ($request->has('products') && is_array($request->products)) {
                foreach ($request->products as $item) {
                    if (!empty($item['product_id']) && !empty($item['quantity'])) {
                        $subtotal = $item['quantity'] * $item['unit_price'];
                        $partsCost += $subtotal;

                        ServiceDetail::create([
                            'service_id' => $service->id,
                            'product_id' => $item['product_id'],
                            'quantity' => $item['quantity'],
                            'unit_price' => $item['unit_price'],
                            'subtotal' => $subtotal,
                        ]);
                    }
                }
            }

            // Update parts cost and total
            $service->update([
                'parts_cost' => $partsCost,
                'total_cost' => $service->service_fee + $partsCost,
                'remaining_payment' => ($service->service_fee + $partsCost) - $service->down_payment,
            ]);

            // Create initial status history
            ServiceStatusHistory::create([
                'service_id' => $service->id,
                'status' => 'menunggu diagnosa',
                'notes' => 'Service baru dibuat',
                'user_id' => Auth::id(),
            ]);

            // Generate QR Code
            $this->generateQrCode($service);

            DB::commit();
            return redirect()->route('services.index')->with('success', 'Service berhasil dibuat');
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()->with('error', 'Gagal membuat service: ' . $e->getMessage());
        }
    }

    /**
     * Display the specified service
     */
    public function show($id)
    {
        $title = "service";
        $service = Service::with(['customer', 'branch', 'technician', 'serviceDetails.product', 'statusHistories.user'])
            ->findOrFail($id);

        return view('transaction.service.show', compact('title', 'service'));
    }

    /**
     * Show the form for editing the specified service
     */
    public function edit($id)
    {
        $title = "service";
        $service = Service::with(['serviceDetails'])->findOrFail($id);
        $customers = Customer::orderBy('name', 'asc')->get();
        $branches = Branch::orderBy('name', 'asc')->get();

        // Get technicians - handle jika role technician belum ada
        try {
            $technicians = User::role('technician')->orderBy('name', 'asc')->get();
        } catch (\Exception $e) {
            // Jika role technician belum ada, ambil semua user
            $technicians = User::orderBy('name', 'asc')->get();
        }

        $products = Product::with(['brand', 'category', 'productStocks', 'productPrices'])->get();

        return view('transaction.service.edit', compact('title', 'service', 'customers', 'branches', 'technicians', 'products'));
    }

    /**
     * Update the specified service in storage
     */
    public function update(Request $request, $id)
    {
        $request->validate([
            'customer_id' => 'required|exists:customers,id',
            'branch_id' => 'required|exists:branches,id',
            'service_date' => 'required|date',
            'device_type' => 'required|string',
            'device_brand' => 'required|string',
            'device_model' => 'required|string',
            'complaint' => 'required|string',
        ]);

        DB::beginTransaction();
        try {
            $service = Service::findOrFail($id);
            $oldStatus = $service->status;

            // Update service
            $service->update([
                'customer_id' => $request->customer_id,
                'branch_id' => $request->branch_id,
                'technician_id' => $request->technician_id,
                'service_date' => $request->service_date,
                'device_type' => $request->device_type,
                'device_brand' => $request->device_brand,
                'device_model' => $request->device_model,
                'imei' => $request->imei,
                'complaint' => $request->complaint,
                'diagnosis' => $request->diagnosis,
                'estimated_cost' => $request->estimated_cost ?? 0,
                'service_fee' => $request->service_fee ?? 0,
                'down_payment' => $request->down_payment ?? 0,
                'priority' => $request->priority ?? 'normal',
                'estimated_completion' => $request->estimated_completion,
                'notes' => $request->notes,
                'status' => $request->status ?? $service->status,
            ]);

            // Delete old service details
            $service->serviceDetails()->delete();

            $partsCost = 0;
            // Save new service details (spare parts)
            if ($request->has('products') && is_array($request->products)) {
                foreach ($request->products as $item) {
                    if (!empty($item['product_id']) && !empty($item['quantity'])) {
                        $subtotal = $item['quantity'] * $item['unit_price'];
                        $partsCost += $subtotal;

                        ServiceDetail::create([
                            'service_id' => $service->id,
                            'product_id' => $item['product_id'],
                            'quantity' => $item['quantity'],
                            'unit_price' => $item['unit_price'],
                            'subtotal' => $subtotal,
                        ]);
                    }
                }
            }

            // Update parts cost and total
            $service->update([
                'parts_cost' => $partsCost,
                'total_cost' => $service->service_fee + $partsCost,
                'remaining_payment' => ($service->service_fee + $partsCost) - $service->down_payment,
            ]);

            // Create status history if status changed
            if ($oldStatus !== $service->status) {
                ServiceStatusHistory::create([
                    'service_id' => $service->id,
                    'status' => $service->status,
                    'notes' => $request->status_notes ?? 'Status diubah',
                    'user_id' => Auth::id(),
                ]);

                // Set completed_at if status is selesai
                if ($service->status === 'selesai') {
                    $service->update(['completed_at' => now()]);
                }
            }

            DB::commit();
            return redirect()->route('services.index')->with('success', 'Service berhasil diupdate');
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()->with('error', 'Gagal mengupdate service: ' . $e->getMessage());
        }
    }

    /**
     * Update service status
     */
    public function updateStatus(Request $request, $id)
    {
        $request->validate([
            'status' => 'required|string',
            'notes' => 'nullable|string',
        ]);

        DB::beginTransaction();
        try {
            $service = Service::findOrFail($id);
            $oldStatus = $service->status;

            // Update status
            $service->update(['status' => $request->status]);

            // Set completed_at if status is selesai
            if ($request->status === 'selesai' && $oldStatus !== 'selesai') {
                $service->update(['completed_at' => now()]);
            }

            // Create status history
            ServiceStatusHistory::create([
                'service_id' => $service->id,
                'status' => $request->status,
                'notes' => $request->notes ?? 'Status diubah menjadi ' . $request->status,
                'user_id' => Auth::id(),
            ]);

            DB::commit();
            return redirect()->back()->with('success', 'Status service berhasil diupdate');
        } catch (\Exception $e) {
            DB::rollBack();
            return redirect()->back()->with('error', 'Gagal mengupdate status: ' . $e->getMessage());
        }
    }

    /**
     * Remove the specified service from storage
     */
    public function destroy($id)
    {
        try {
            $service = Service::findOrFail($id);
            $service->delete();

            return redirect()->route('services.index')->with('success', 'Service berhasil dihapus');
        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Gagal menghapus service: ' . $e->getMessage());
        }
    }

    /**
     * Print service receipt
     */
    public function print($id)
    {
        $service = Service::with(['customer', 'branch', 'technician', 'serviceDetails.product'])
            ->findOrFail($id);

        $pdf = Pdf::loadView('transaction.service.print', compact('service'));
        return $pdf->stream('service-' . $service->service_number . '.pdf');
    }

    /**
     * Generate unique barcode for service
     */
    private function generateBarcode()
    {
        $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);

        do {
            $randomString = '';
            for ($i = 0; $i < 100; $i++) {
                $randomString .= $characters[rand(0, $charactersLength - 1)];
            }

            $barcode = 'SRV' . date('ymd') . $randomString;

            // Check if barcode already exists
            $exists = Service::where('barcode', $barcode)->exists();
        } while ($exists);

        return $barcode;
    }

    /**
     * Generate QR Code for service
     */
    private function generateQrCode(Service $service)
    {
        try {
            $qrPath = public_path('qrcodes/services');
            if (!file_exists($qrPath)) {
                mkdir($qrPath, 0755, true);
            }

            // Generate SVG QR Code
            $svgRenderer = new ImageRenderer(
                new RendererStyle(300, 10),
                new SvgImageBackEnd()
            );
            $svgWriter = new Writer($svgRenderer);
            $svgWriter->writeFile($service->barcode, $qrPath . '/service_' . $service->id . '.svg');

            // Generate PNG QR Code using QR matrix
            $qrCode = Encoder::encode($service->barcode, ErrorCorrectionLevel::L(), 'UTF-8');
            $matrix = $qrCode->getMatrix();

            $moduleSize = 15;
            $quietZone = 4;
            $matrixWidth = $matrix->getWidth();

            $imageWidth = ($matrixWidth + ($quietZone * 2)) * $moduleSize;
            $imageHeight = $imageWidth;

            $img = imagecreatetruecolor($imageWidth, $imageHeight);

            $white = imagecolorallocate($img, 255, 255, 255);
            $black = imagecolorallocate($img, 0, 0, 0);

            imagefill($img, 0, 0, $white);

            for ($y = 0; $y < $matrixWidth; $y++) {
                for ($x = 0; $x < $matrixWidth; $x++) {
                    if ($matrix->get($x, $y) === 1) {
                        $posX = ($x + $quietZone) * $moduleSize;
                        $posY = ($y + $quietZone) * $moduleSize;

                        imagefilledrectangle(
                            $img,
                            $posX,
                            $posY,
                            $posX + $moduleSize - 1,
                            $posY + $moduleSize - 1,
                            $black
                        );
                    }
                }
            }

            $pngFilePath = $qrPath . '/service_' . $service->id . '.png';
            imagepng($img, $pngFilePath, 0);
            imagedestroy($img);

            return true;
        } catch (\Exception $e) {
            \Log::error('Failed to generate QR Code for service ' . $service->id . ': ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Download QR Code as JPG
     */
    public function downloadQrCodeJpg($id)
    {
        $service = Service::findOrFail($id);

        if (!$service->barcode) {
            return redirect()->back()->with('error', 'QR Code belum tersedia untuk service ini');
        }

        try {
            // Generate QR Code matrix using BaconQrCode
            $qrCode = \BaconQrCode\Encoder\Encoder::encode(
                $service->barcode,
                \BaconQrCode\Common\ErrorCorrectionLevel::L(),
                'UTF-8'
            );
            $matrix = $qrCode->getMatrix();

            $moduleSize = 15;
            $quietZone = 4;
            $matrixWidth = $matrix->getWidth();

            $imageWidth = ($matrixWidth + ($quietZone * 2)) * $moduleSize;

            // Create image with white background
            $img = imagecreatetruecolor($imageWidth, $imageWidth);
            $white = imagecolorallocate($img, 255, 255, 255);
            $black = imagecolorallocate($img, 0, 0, 0);

            // Fill background
            imagefill($img, 0, 0, $white);

            // Draw QR code modules
            for ($y = 0; $y < $matrixWidth; $y++) {
                for ($x = 0; $x < $matrixWidth; $x++) {
                    if ($matrix->get($x, $y) === 1) {
                        $xPos = ($x + $quietZone) * $moduleSize;
                        $yPos = ($y + $quietZone) * $moduleSize;
                        imagefilledrectangle($img, $xPos, $yPos, $xPos + $moduleSize - 1, $yPos + $moduleSize - 1, $black);
                    }
                }
            }

            // Save to temporary file
            $tempJpgPath = sys_get_temp_dir() . '/qr_service_' . $service->id . '_' . time() . '.jpg';
            imagejpeg($img, $tempJpgPath, 95);
            imagedestroy($img);

            // Download file and delete after
            $fileName = 'QRCode-Service-' . $service->service_number . '.jpg';

            return response()->download($tempJpgPath, $fileName, [
                'Content-Type' => 'image/jpeg'
            ])->deleteFileAfterSend(true);

        } catch (\Exception $e) {
            abort(500, 'Gagal generate QR Code JPG: ' . $e->getMessage());
        }
    }

    /**
     * Find service by barcode (for QR scanning)
     */
    public function findServiceByBarcode(Request $request)
    {
        $barcode = $request->input('barcode');

        if (!$barcode) {
            return response()->json([
                'success' => false,
                'message' => 'QR Code tidak valid'
            ], 400);
        }

        $service = Service::where('barcode', $barcode)
            ->with(['customer', 'branch', 'technician'])
            ->first();

        if (!$service) {
            return response()->json([
                'success' => false,
                'message' => 'Service tidak ditemukan'
            ], 404);
        }

        return response()->json([
            'success' => true,
            'service' => [
                'id' => $service->id,
                'service_number' => $service->service_number,
                'barcode' => $service->barcode,
                'customer_name' => $service->customer->name ?? '-',
                'branch_name' => $service->branch->name ?? '-',
                'technician_name' => $service->technician->name ?? '-',
                'device_type' => $service->device_type,
                'device_brand' => $service->device_brand,
                'device_model' => $service->device_model,
                'service_date' => $service->service_date->format('d/m/Y'),
                'total_cost' => number_format($service->total_cost, 0, ',', '.'),
                'status' => $service->status,
                'url' => route('services.show', $service->id)
            ]
        ]);
    }
}
