<?php

namespace App\Http\Controllers;

use App\Models\Item;
use App\Models\Plant;
use App\Models\WeightValidation;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Str;

class ObdController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index() {}

    /**
     * Store a newly created resource in storage.
     */
    public function store_obd(Request $request)
    {
        $expectedUser = env('API_AUTH_USER');
        $expectedPw = env('API_AUTH_PW');

        $header_auth = $request->header('Authorization');
        $expectedToken = $expectedUser.':'.$expectedPw;

        // if("Bearer " . $expectedToken != $header_auth)
        // {
        //     return response("ERROR: Unauthorized", 403)
        //         ->header('Content-Type', 'text/plain');
        // }
        if ('Bearer '.$expectedToken != $header_auth) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => 'Invalid authorization token!',
            ], 403);
        }

        try {
            $data = $request->all();

            $plantCode = $data['plant_code'] ?? '';
            $obdNumber = $data['obd_number'] ?? '';

            Log::info('OBD POST API >>', ['Post-Data' => $data]);

            // Validate required fields
            if (! $plantCode || $plantCode == null || $plantCode == '') {
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "Plant code can't be empty!",
                ], 404);
            } elseif (! is_numeric($plantCode) || Str::length($plantCode) < 4 || ! preg_match('/^[1-9]\d{3,}$/', $plantCode)) { // !ctype_digit($data['plant_code'])
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "Invalid plant code '{$plantCode}' found!",
                ], 404);
            } elseif (! $obdNumber || $obdNumber == null || $obdNumber == '') {
                // return response("ERROR: OBD Number can't be empty", 400)
                // ->header('Content-Type', 'text/plain');
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "OBD number can't be empty!",
                ], 400);
            } elseif (Str::length($obdNumber) < 8) {
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "OBD number '{$obdNumber}' should contain minimum 8 digits!",
                ], 404);
            } elseif (! ctype_alnum($obdNumber)) {
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "OBD number '{$obdNumber}' should contain only alpha-numeric values!",
                ], 404);
            } elseif (! preg_match('/^[a-zA-Z1-9][a-zA-Z0-9]{8,}$/', $obdNumber)) {
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "OBD number '{$obdNumber}' should not begin with '0'!",
                ], 404);
            } elseif (empty($data['line_numbers'])) {
                // return response($message, 400)->header('Content-Type', 'text/plain');
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => 'Missing required field(s): line_numbers',
                ], 400);
            }

            // Lookup plant_id by plant code
            $plant = Plant::where('code', $plantCode)->first();
            if (! $plant) {
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "Plant code '{$plantCode}' not found!",
                ], 404);
            }
            $plantId = $plant?->id ?? null;
            $plantName = $plant?->name ?? null;

            // Check if OBD number exists for that plant
            $obdRecords = WeightValidation::where('plant_id', $plantId)
                ->where('obd_number', $obdNumber)
                ->exists();

            if (! $obdRecords) {
                // return response( "ERROR: OBD Number '$obdNumber' not found for plant '{$plantCode}'",404)->header('Content-Type', 'text/plain');
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => "OBD Number '{$obdNumber}' not found for the plant '{$plantName}'!",
                ], 404);
            }

            $missingLines = [];
            $alreadyUpdatedLines = [];

            foreach ($data['line_numbers'] as $line) {
                $lineNumber = $line['line_number'] ?? null;
                if ($lineNumber == '' || $lineNumber == null) {
                    // return response("ERROR: Line Number can't be empty", 400)
                    //     ->header('Content-Type', 'text/plain');
                    return response()->json([
                        'status_code' => 'ERROR',
                        'status_description' => "Line number can't be empty!",
                    ], 404);
                }

                $record = WeightValidation::where('plant_id', $plantId)
                    ->where('obd_number', $obdNumber)
                    ->where('line_number', $lineNumber)
                    ->first();

                if (! $record) {
                    $missingLines[] = $lineNumber;

                    continue;
                }

                $mandatoryFields = ['vehicle_number', 'bundle_number', 'heat_number', 'picked_weight', 'scanned_by'];
                $missingFields = [];

                foreach ($mandatoryFields as $field) {
                    if ($line[$field] == '' || $line[$field] == null) {
                        $missingFields[] = ucwords(str_replace('_', ' ', $field));
                    }
                }

                if (! empty($missingFields)) {
                    if (count($missingFields) == 1) {
                        $fieldsString = $missingFields[0];
                    } else {
                        $lastField = array_pop($missingFields);
                        $fieldsString = implode(', ', $missingFields).' and '.$lastField;
                    }

                    // return response($message, 400)->header('Content-Type', 'text/plain');
                    return response()->json([
                        'status_code' => 'ERROR',
                        'status_description' => $fieldsString." can't be empty for line_number {$lineNumber}!",
                    ], 400);
                }
            }

            if (! empty($missingLines)) {
                // return response($message, 404)->header('Content-Type', 'text/plain');
                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => 'Line(s) '.implode(', ', $missingLines)." not found for the plant '{$plantName}' and OBD number: '{$obdNumber}'!",
                ], 404);
            }

            $seenPairs = [];
            $internalDuplicates = [];
            $bundleChecks = [];

            // Check for duplicates within the request
            foreach ($data['line_numbers'] as $line) {
                $lineNumber = $line['line_number'] ?? null;
                $bundleNumber = trim((string) $line['bundle_number'] ?? '');
                $pairKey = $lineNumber.'|'.$bundleNumber;

                if (isset($seenPairs[$pairKey])) {
                    $internalDuplicates[] = "Line Number {$lineNumber} with Bundle Number {$bundleNumber}";
                } else {
                    $seenPairs[$pairKey] = true;
                    $bundleChecks[] = [
                        'line_number' => $lineNumber,
                        'bundle_number' => $bundleNumber,
                    ];
                }
            }

            // Check for duplicates in the database

            $bundleNumbers = array_column($bundleChecks, 'bundle_number');
            $lineNumbers = array_column($bundleChecks, 'line_number');

            $existingBundles = WeightValidation::where('plant_id', $plantId)
                ->where('obd_number', $obdNumber)
                ->whereIn('line_number', $lineNumbers)
                ->whereIn('bundle_number', $bundleNumbers)
                ->get(['line_number', 'bundle_number']);

            $grouped = [];
            foreach ($existingBundles as $row) {
                $line = $row->line_number;
                $bundle = $row->bundle_number;

                if (! isset($grouped[$line])) {
                    $grouped[$line] = [];
                }
                if ($bundle && ! in_array($bundle, $grouped[$line])) {
                    $grouped[$line][] = $bundle;
                }
            }

            $dbDuplicates = [];
            foreach ($grouped as $line => $bundles) {
                $bundlesStr = implode(', ', $bundles);
                $dbDuplicates[] = "Line {$line}, has bundle numbers : {$bundlesStr}";
            }

            // Return all errors if any duplicates found
            $allDuplicates = [];
            if (! empty($internalDuplicates)) {
                $allDuplicates[] = 'Duplicate(s) '.implode(', ', $internalDuplicates);
            }
            if (! empty($dbDuplicates)) {
                $allDuplicates[] = 'Already exists in database: '.implode('; ', $dbDuplicates);
            }

            if (! empty($allDuplicates)) {
                // return response(
                //     "Error:" . implode("\n", $allDuplicates),
                //     400
                // )->header('Content-Type', 'text/plain');

                return response()->json([
                    'status_code' => 'ERROR',
                    'status_description' => implode(', and ', $allDuplicates),
                ], 400);
            }

            // ..
            $updated = 0;
            $inserted = 0;
            $updatedLines = [];
            $insertedLines = [];
            $lineTracker = [];

            foreach ($data['line_numbers'] as $line) {
                $lineNumber = $line['line_number'] ?? null;
                $existing = WeightValidation::where('plant_id', $plantId)
                    ->where('obd_number', $obdNumber)
                    ->where('line_number', $lineNumber)
                    ->where(function ($query) {
                        $query->whereNull('bundle_number')
                            ->orWhere('bundle_number', '');
                    })
                    ->first();

                if ($existing) {
                    $existing->update([
                        'vehicle_number' => $line['vehicle_number'] ?? null,
                        'bundle_number' => $line['bundle_number'] ?? null,
                        'heat_number' => $line['heat_number'] ?? null,
                        'picked_weight' => $line['picked_weight'] ?? null,
                        'scanned_by' => $line['scanned_by'] ?? null,
                        'updated_at' => now(),
                    ]);
                    $updated++;
                    $updatedLines[] = $lineNumber;
                    $lineTracker[$lineNumber] = 1;
                } else {
                    $original = WeightValidation::where('plant_id', $plantId)
                        ->where('obd_number', $obdNumber)
                        ->where('line_number', $lineNumber)
                        ->orderBy('id')
                        ->first();
                    // where(['plant_id' => $plantId, 'obd_number' => $obdNumber, 'line_number' => $lineNumber])

                    WeightValidation::create([
                        'plant_id' => $plantId,
                        'obd_number' => $obdNumber,
                        'line_number' => $lineNumber,
                        'item_id' => $original->item_id ?? null,
                        'vehicle_number' => $line['vehicle_number'] ?? null,
                        'bundle_number' => $line['bundle_number'] ?? null,
                        'picked_weight' => $line['picked_weight'] ?? null,
                        'scanned_by' => $line['scanned_by'] ?? null,
                        'batch_number' => $original->batch_number ?? null,
                        'heat_number' => $line['heat_number'] ?? null,
                        'obd_weight' => $original->obd_weight ?? null,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);

                    $inserted++;
                    $insertedLines[] = $lineNumber;
                    $lineTracker[$lineNumber] = isset($lineTracker[$lineNumber]) ? $lineTracker[$lineNumber] + 1 : 1;
                }
            }

            $responseMessage = "OBD Number '{$obdNumber}'";

            if ($updated > 0) {
                $responseMessage .= ' updated successfully. Line Numbers: {'.implode(', ', $updatedLines).'}';
            }

            if ($inserted > 0) {
                $responseMessage .= ' inserted successfully. Line Numbers: {'.implode(', ', $insertedLines).'}';
            }

            // return response($responseMessage, 200)
            //     ->header('Content-Type', 'text/plain');
            return response()->json([
                'status_code' => 'SUCCESS',
                'status_description' => $responseMessage,
            ], 200);

        } catch (\Exception $e) {
            // response("ERROR: Server error", 500)->header('Content-Type', 'text/plain');
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => 'Store OBD data internal server error : '.$e?->getCode(),
            ], 500);
        }
    }

    public function get_test(Request $request)
    {
        $expectedUser = env('API_AUTH_USER');
        $expectedPw = env('API_AUTH_PW');

        $header_auth = $request->header('Authorization');
        $expectedToken = $expectedUser.':'.$expectedPw;

        if ('Bearer '.$expectedToken != $header_auth) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => 'Invalid authorization token!',
            ], 403);
        }

        // $plantCode = $request->header('plant-code');
        // if (empty($plantCode))
        // {
        //     return response()->json([
        //         'status_code' => 'ERROR',
        //         'status_description' => "Plant code can't be empty"
        //     ], 400);
        // }
        // else if(Str::length($plantCode) < 4 || !is_numeric($plantCode))
        // {
        //     return response()->json([
        //         'status_code' => 'ERROR',
        //         'status_description' => "Plant code should contain minimum 4 digits numeric values only!"
        //     ], 400);
        // }

        // $plantId = Plant::where('code', $plantCode)->value('id');

        // if (!$plantId)
        // {
        //     return response()->json([
        //         'status_code' => 'ERROR',
        //         'status_description' => "Plant code not found"
        //     ], 400);
        // }

        $productionOrder = $request->header('production-order');

        if (empty($productionOrder)) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Production order can't be empty!",
            ], 400);
        } elseif (Str::length($productionOrder) < 7 || ! is_numeric($productionOrder)) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Production order '{$productionOrder}' should contain minimum 7 digits numeric values only!",
            ], 400);
        }

        $prodOrderExist = ($productionOrder == '1234567' || $productionOrder == '7654321');

        if (! $prodOrderExist) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Production order '{$productionOrder}' not found!",
            ], 400);
        }

        try {
            // return response("Successfully GET request Received", 200)->header('Content-Type', 'text/plain');
            $itemCode = Item::where('code', '123456')->where('plant_id', 1)->first();

            return response()->json([
                'item_code' => $itemCode->code,
                'item_description' => $itemCode->description,
            ], 200);
        } catch (\Exception $e) {
            // return response("ERROR: GET test data server error", 500)->header('Content-Type', 'text/plain');
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => 'Get test data internal server error : '.$e?->getCode(),
            ], 500);
        }
    }

    public function get_obd(Request $request)
    {
        $expectedUser = env('API_AUTH_USER');
        $expectedPw = env('API_AUTH_PW');
        $header_auth = $request->header('Authorization');
        $expectedToken = $expectedUser.':'.$expectedPw;

        // if ("Bearer " . $expectedToken != $header_auth)
        // {
        //     return response("ERROR: Unauthorized", 403)
        //         ->header('Content-Type', 'text/plain');
        // }

        if ('Bearer '.$expectedToken != $header_auth) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => 'Invalid authorization token!',
            ], 403);
        }

        $plantCode = $request->header('plant-code');
        $obdNumber = $request->header('obd-number');

        Log::info('OBD GET API >>', ['plant-code' => $plantCode, 'obd-number' => $obdNumber]);

        if (! $plantCode || $plantCode == null || $plantCode == '') {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Plant code can't be empty!",
            ], 404);
        } elseif (! is_numeric($plantCode) || Str::length($plantCode) < 4 || ! preg_match('/^[1-9]\d{3,}$/', $plantCode)) { // !ctype_digit($data['plant_code'])
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Invalid plant code '{$plantCode}' found!",
            ], 404);
        } elseif (! $obdNumber || $obdNumber == null || $obdNumber == '') {
            // return response("ERROR: OBD Number can't be empty", 400)
            // ->header('Content-Type', 'text/plain');
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "OBD number can't be empty!",
            ], 400);
        } elseif (Str::length($obdNumber) < 8) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "OBD number '{$obdNumber}' should contain minimum 8 digits!",
            ], 404);
        } elseif (! ctype_alnum($obdNumber)) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "OBD number '{$obdNumber}' should contain only alpha-numeric values!",
            ], 404);
        } elseif (! preg_match('/^[a-zA-Z1-9][a-zA-Z0-9]{8,}$/', $obdNumber)) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "OBD number '{$obdNumber}' should not begin with '0'!",
            ], 404);
        }

        // Fetch the plant id by code
        $plant = Plant::where('code', $plantCode)->first();
        if (! $plant) {
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "Plant code '{$plantCode}' not found!",
            ], 404);
        }
        $plantId = $plant?->id ?? null;
        $plantName = $plant?->name ?? null;

        // $records = WeightValidation::where('plant_id', $plantId)
        //     ->where('obd_number', $obdNumber)
        //     ->get();
        // Fetch and filter records where vehicle_number is empty or null

        $exists = WeightValidation::where('plant_id', $plantId)
            ->where('obd_number', $obdNumber)
            ->exists();

        if (! $exists) {
            // return response("ERROR: OBD number $obdNumber does not exist for plant '$plantCode'", 404)
            //     ->header('Content-Type', 'text/plain');
            return response()->json([
                'status_code' => 'ERROR',
                'status_description' => "OBD number '{$obdNumber}' does not exist for plant '{$plantName}'!",
            ], 400);
        }

        $records = WeightValidation::where('plant_id', $plantId)
            ->where('obd_number', $obdNumber)
            ->get()
            ->filter(function ($record) {
                return $record->vehicle_number == '' || $record->vehicle_number == null;
            });

        if ($records->isEmpty()) {
            // return response("SUCCESS: Already scanning process completed for the OBD Number", 200)->header('Content-Type', values: 'text/plain');
            return response()->json([
                'status_code' => 'SUCCESS',
                'status_description' => "Already weight validation completed for the OBD Number '{$obdNumber}'!",
            ], 200);
        }

        $itemIds = $records->pluck('item_id')->unique();
        $itemCodes = Item::whereIn('id', $itemIds)
            ->select('id', 'code', 'description')
            ->get()
            ->keyBy('id');

        $ObdResponseStructure = [
            'OBD_Number' => [
                [
                    'OBD_Number' => $obdNumber,
                    'Line_Numbers' => $records->map(function ($item) use ($itemCodes) {
                        $itemInfo = $itemCodes[$item->item_id] ?? null;

                        return [
                            'Line' => $item->line_number ?? '',
                            'Material_Code' => $itemInfo->code ?? '',
                            'Material_Description' => $itemInfo->description ?? '',
                            'Batch_Number' => $item->batch_number ?? '',
                            // 'Heat_Number' => $item->heat_number ?? "",
                            'Actual_Weight' => $item->obd_weight ?? '',
                        ];
                    })->values()->toArray(),
                ],
            ],
        ];

        return response()->json($ObdResponseStructure);
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        //
    }
}
