<?php

namespace App\Http\Controllers;

use App\Models\District;
use App\Models\Nationality;
use App\Models\Parish;
use App\Models\Region;
use App\Models\SubCounty;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class LocationController extends Controller
{
    private function resolveLocationRecord(string $modelClass, $reference)
    {
        if ($reference === null || $reference === '') {
            return null;
        }

        $candidates = [$reference];
        try {
            $decrypted = decrypt($reference);
            $candidates[] = $decrypted;
        } catch (\Throwable $e) {
            // Reference is not encrypted; continue with raw UUID/numeric value.
        }

        foreach (array_unique($candidates) as $candidate) {
            if (Schema::hasColumn((new $modelClass)->getTable(), 'uuid')) {
                $record = $modelClass::where('uuid', $candidate)->first();
                if ($record) {
                    return $record;
                }
            }

            if (is_numeric($candidate)) {
                $record = $modelClass::find($candidate);
                if ($record) {
                    return $record;
                }
            }
        }

        return null;
    }

    private function publicLocationKey($record)
    {
        return !empty($record->uuid) ? $record->uuid : $record->id;
    }

    private function ensureLocationUuid($record): void
    {
        if (Schema::hasColumn($record->getTable(), 'uuid') && empty($record->uuid)) {
            $record->uuid = (string) Str::uuid();
        }
    }

    private function setIfColumn($record, string $column, $value): void
    {
        if (Schema::hasColumn($record->getTable(), $column)) {
            $record->{$column} = $value;
        }
    }

    private function setDistrictCoordinatesAndNotes(District $district, Request $request): void
    {
        $request->validate([
            'latitude' => ['nullable', 'numeric', 'between:-90,90'],
            'latitudes' => ['nullable', 'numeric', 'between:-90,90'],
            'longitude' => ['nullable', 'numeric', 'between:-180,180'],
            'longitudes' => ['nullable', 'numeric', 'between:-180,180'],
            'notes' => ['nullable', 'string', 'max:3000'],
        ]);

        // Existing NGBVD databases use plural column names: latitudes / longitudes.
        // Some newer forms submit latitude / longitude. Map safely without writing
        // to columns that do not exist, otherwise district updates fail.
        if (Schema::hasColumn($district->getTable(), 'latitudes')) {
            $district->latitudes = $request->input('latitude', $request->input('latitudes'));
        } elseif (Schema::hasColumn($district->getTable(), 'latitude')) {
            $district->latitude = $request->input('latitude', $request->input('latitudes'));
        }

        if (Schema::hasColumn($district->getTable(), 'longitudes')) {
            $district->longitudes = $request->input('longitude', $request->input('longitudes'));
        } elseif (Schema::hasColumn($district->getTable(), 'longitude')) {
            $district->longitude = $request->input('longitude', $request->input('longitudes'));
        }

        $this->setIfColumn($district, 'notes', $request->notes);
    }

    //
    function nationality(Request $request){
        $request->validate(['id' => ['nullable', 'string', 'max:255']]);
        $details = [];
        if ($request->filled('id')) {
            $servicedetails = $this->resolveLocationRecord(Nationality::class, $request->input('id'));
            if (!$servicedetails) {
                abort(404, 'Nationality record not found.');
            }
            $details = $servicedetails->toArray();
        }
        return view('location.nationality', compact('details'));
    }

    function postnationality(Request $request){
        $request->validate([
            'id' => ['nullable', 'string', 'max:255'],
            'name' => ['required', 'string', 'max:150'],
            'code' => ['nullable', 'string', 'max:50'],
            'notes' => ['nullable', 'string', 'max:3000'],
        ]);

        $formvalues = $request; $serviceModel = new Nationality();
        $now = date_create('now', timezone_open('Africa/Kampala'))->format('Y-m-d H:i:s');
        $userid = Auth::user()->id;

        $postdata = $serviceModel->processvalues($formvalues, $userid, $now);
        if(isset($postdata['success_message'])){
            $logging = $this->auditrail;
            $logdata['resource'] = "Location [Nationality]";
            $logdata['action'] = (!empty($formvalues['id']) ? "Updating Nationality [" : "Creating new Nationality [") . $formvalues['name'] . "]";
            $logdata['response'] = !empty($formvalues['id']) ? 'Updating Nationality has been successful' : 'Adding a new Nationality has been successful';
            $logdata['status'] = "1";
            $logdata['ip_address'] = \Request::ip();
            $logdata['mac_address'] = 'Unavailable';
            $logdata['browser'] = request()->userAgent();
            $logdata['date'] = date("Y-m-d", strtotime($now));
            $logdata['now'] = $now;
            $logdata['userid'] = Auth::user()->id;
            $logging->postLogData($logdata);

            return back()->with('success_message', $postdata['success_message']);
        }else{
            $logging = $this->auditrail;
            $logdata['resource'] = "Location [Nationality]";
            $logdata['action'] = (!empty($formvalues['id']) ? "Updating Nationality [" : "Creating new Nationality [") . $formvalues['name'] . "]";
            $logdata['response'] = !empty($formvalues['id']) ? 'Updating Nationality has failed' : 'Adding a new Nationality has failed';
            $logdata['status'] = "0";
            $logdata['ip_address'] = \Request::ip();
            $logdata['mac_address'] = 'Unavailable';
            $logdata['browser'] = request()->userAgent();
            $logdata['date'] = date("Y-m-d", strtotime($now));
            $logdata['now'] = $now;
            $logdata['userid'] = Auth::user()->id;
            $logging->postLogData($logdata);

            return back()->with('error_message', $postdata['error_message']);
        }
    }

    function region(Request $request){
        $request->validate(['id' => ['nullable', 'string', 'max:255']]);
        $details = [];
        if ($request->filled('id')) {
            $region = $this->resolveLocationRecord(Region::class, $request->input('id'));
            if (!$region) {
                abort(404, 'Region record not found.');
            }
            $details = $region->toArray();
        }
        return view('location.region', compact('details'));
    }


    function district(Request $request){
        $regions = Region::all();
        $details = [];
        if ($request->filled('id')) {
            $district = $this->resolveLocationRecord(District::class, $request->input('id'));
            if (!$district) {
                abort(404, 'District record not found.');
            }
            $details = $district->toArray();
        }
    	return view('location.district')->with(['regions' => $regions, 'details' => $details]);
    }

    function subcounty(Request $request){
        $regions = Region::all();
        $districts = District::select('name', 'id')->get();
        $details = [];
        if ($request->filled('id')) {
            $subcounty = $this->resolveLocationRecord(SubCounty::class, $request->input('id'));
            if (!$subcounty) {
                abort(404, 'Sub-county record not found.');
            }
            $details = $subcounty->toArray();
        }
    	return view('location.sub-county')->with(['districts' => $districts, 'regions' => $regions, 'details' => $details]);
    }

    function parish(Request $request){
        $regions = Region::all();
        $districts = District::select('name', 'id')->get();
        $subcounties = SubCounty::select('name', 'id')->get();
        $details = [];
        if ($request->filled('id')) {
            $parish = $this->resolveLocationRecord(Parish::class, $request->input('id'));
            if (!$parish) {
                abort(404, 'Parish record not found.');
            }
            $details = $parish->toArray();
        }
    	return view('location.parish')->with(['districts' => $districts, 'regions' => $regions, 'subcounties' => $subcounties, 'details' => $details]);
    }

    function list(Request $request, $locationType){
        //return $locationType;
        $locationData = array();
        if($locationType == 'nationality'){
            $locationData = DB::table('nationality as n')
                ->select('n.*')
                ->get();
        }

        if($locationType == 'region'){
            $locationData = DB::table('regions as r')
                ->select('r.*')
                ->get();
        }

        if($locationType == 'subregion'){
            $locationData = DB::table('sub_regions as sr')
                ->select('sr.*', 'r.name as region', 'sr.name as subregion')
                ->leftJoin('regions as r', 'sr.regions_id', '=', 'r.id')
                ->get();
        }

        if($locationType == 'district' || Empty($locationType)){
            $locationData = DB::table('districts as ds')
                ->select('ds.*', 'r.name as region', 'sr.name as subregion')
                ->leftJoin('regions as r', 'ds.regions_id', '=', 'r.id')
                ->leftJoin('sub_regions as sr', 'ds.sub_regions_id', '=', 'sr.id')
                ->get();
        }

        if($locationType == 'subcounty'){
            $locationData = DB::table('sub_counties as sc')
                ->select('sc.*', 'd.name as district', 'r.name as region', 'sr.name as subregion')
                ->leftJoin('districts as d', 'sc.districts_id', '=', 'd.id')
                ->leftJoin('regions as r', 'sc.regions_id', '=', 'r.id')
                ->leftJoin('sub_regions as sr', 'sc.sub_regions_id', '=', 'sr.id')
                ->get();
        }

        if($locationType == 'parish'){
            $locationData = DB::table('parishes as p')
                ->select('p.*', 'sc.name as subcounty', 'd.name as district', 'r.name as region', 'sr.name as subregion')
                ->leftJoin('sub_counties as sc', 'p.sub_counties_id', '=', 'sc.id')
                ->leftJoin('districts as d', 'p.districts_id', '=', 'd.id')
                ->leftJoin('regions as r', 'p.regions_id', '=', 'r.id')
                ->leftJoin('sub_regions as sr', 'p.sub_regions_id', '=', 'sr.id')
                ->get();
        }

        return view('location.list', compact('locationData'))->with('tab', $locationType);
    }


    public function getNationality($id)
    {
        $nationality = $this->resolveLocationRecord(Nationality::class, $id);
        if (!$nationality) {
            abort(404, 'Nationality record not found.');
        }

        return redirect('/location/nationality?id=' . $this->publicLocationKey($nationality));
    }

    public function getRegion($id)
    {
        $region = $this->resolveLocationRecord(Region::class, $id);
        if (!$region) {
            abort(404, 'Region record not found.');
        }

        return redirect('/location/regions?id=' . $this->publicLocationKey($region));
    }

    public function getSubCounty($id)
    {
        $subcounty = $this->resolveLocationRecord(SubCounty::class, $id);
        if (!$subcounty) {
            abort(404, 'Sub-county record not found.');
        }

        return redirect('/location/sub-county?id=' . $this->publicLocationKey($subcounty));
    }

    public function getParish($id)
    {
        $parish = $this->resolveLocationRecord(Parish::class, $id);
        if (!$parish) {
            abort(404, 'Parish record not found.');
        }

        return redirect('/location/parish?id=' . $this->publicLocationKey($parish));
    }

    /* ================= REGION CRUD ================= */
    public function storeRegion(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:150|unique:regions,name',
        ]);

        $region = new Region();
        $region->name = $request->name;
        $region->created_by = Auth::id();
        $this->ensureLocationUuid($region);
        $region->save();

        return back()->with('success_message', 'Region added successfully');
    }

    public function updateRegion(Request $request, $id)
    {
        $region = $this->resolveLocationRecord(Region::class, $id);
        if (!$region) {
            abort(404, 'Region record not found.');
        }

        $request->validate([
            'name' => 'required|string|max:150|unique:regions,name,' . $region->id,
        ]);

        $region->name = $request->name;
        $region->updated_by = Auth::id();
        $region->save();

        return back()->with('success_message', 'Region updated successfully');
    }

    public function deleteRegion($id)
    {
        $region = $this->resolveLocationRecord(Region::class, $id);
        if (!$region) {
            abort(404, 'Region record not found.');
        }
        $region->delete();
        return back()->with('success_message', 'Region deleted successfully');
    }


    /* ================= DISTRICT CRUD ================= */
    public function storeDistrict(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:150|unique:districts,name',
            'regions_id' => 'required|exists:regions,id',
        ]);

        $district = new District();
        $district->name = $request->name;
        $district->code = $request->code;
        $district->regions_id = $request->regions_id;
        $this->setDistrictCoordinatesAndNotes($district, $request);
        $district->created_by = Auth::id();
        $this->ensureLocationUuid($district);
        $district->save();

        return back()->with('success_message', 'District added successfully');
    }

    public function getDistrict($id)
    {
        $district = $this->resolveLocationRecord(District::class, $id);
        if (!$district) {
            abort(404, 'District record not found.');
        }

        return redirect('/location/districts?id=' . $this->publicLocationKey($district));
    }

    public function updateDistrict(Request $request, $id)
    {
        $district = $this->resolveLocationRecord(District::class, $id);
        if (!$district) {
            abort(404, 'District record not found.');
        }

        $request->validate([
            'name' => 'required|string|max:150|unique:districts,name,' . $district->id,
            'regions_id' => 'required|exists:regions,id',
        ]);

        $district->name = $request->name;
        $district->code = $request->code;
        $district->regions_id = $request->regions_id;
        $this->setDistrictCoordinatesAndNotes($district, $request);
        $district->updated_by = Auth::id();
        $this->ensureLocationUuid($district);
        $district->save();

        return redirect('/location/list/district')->with('success_message', 'District updated successfully');
    }

    public function deleteDistrict($id)
    {
        $district = $this->resolveLocationRecord(District::class, $id);
        if (!$district) {
            abort(404, 'District record not found.');
        }
        $district->delete();
        return back()->with('success_message', 'District deleted successfully');
    }


    /* ================= SUB-COUNTY CRUD ================= */
    public function storeSubCounty(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:150|unique:sub_counties,name',
            'districts_id' => 'required|exists:districts,id',
        ]);

        $subcounty = new SubCounty();
        $subcounty->name = $request->name;
        $subcounty->districts_id = $request->districts_id;
        $subcounty->regions_id = $request->regions_id;
        $subcounty->created_by = Auth::id();
        $this->ensureLocationUuid($subcounty);
        $subcounty->save();

        return back()->with('success_message', 'SubCounty added successfully');
    }

    public function updateSubCounty(Request $request, $id)
    {
        $subcounty = $this->resolveLocationRecord(SubCounty::class, $id);
        if (!$subcounty) {
            abort(404, 'Sub-county record not found.');
        }

        $request->validate([
            'name' => 'required|string|max:150|unique:sub_counties,name,' . $subcounty->id,
            'districts_id' => 'required|exists:districts,id',
        ]);

        $subcounty->name = $request->name;
        $subcounty->districts_id = $request->districts_id;
        $subcounty->regions_id = $request->regions_id;
        $subcounty->updated_by = Auth::id();
        $subcounty->save();

        return back()->with('success_message', 'SubCounty updated successfully');
    }

    public function deleteSubCounty($id)
    {
        $subcounty = $this->resolveLocationRecord(SubCounty::class, $id);
        if (!$subcounty) {
            abort(404, 'Sub-county record not found.');
        }
        $subcounty->delete();
        return back()->with('success_message', 'SubCounty deleted successfully');
    }


    /* ================= PARISH CRUD ================= */
    public function storeParish(Request $request)
    {
        //max:150|unique:parishes,name
        $request->validate([
            'name' => ['required', 'string', 'max:150'],
            'districts_id' => ['required', 'integer', 'exists:districts,id'],
            'sub_counties_id' => ['required', 'integer', 'exists:sub_counties,id'],
            'regions_id' => ['nullable', 'integer', 'exists:regions,id'],
        ]);

        $parish = new Parish();
        $parish->name = $request->name;
        $parish->sub_counties_id = $request->sub_counties_id;
        $parish->districts_id = $request->districts_id;
        $parish->regions_id = $request->regions_id;
        $parish->created_by = Auth::id();
        $this->ensureLocationUuid($parish);
        $parish->save();

        return back()->with('success_message', 'Parish added successfully');
    }

    public function updateParish(Request $request, $id)
    {
        $parish = $this->resolveLocationRecord(Parish::class, $id);
        if (!$parish) {
            abort(404, 'Parish record not found.');
        }

        $request->validate([
            'name' => ['required', 'string', 'max:150'],
            'districts_id' => ['required', 'integer', 'exists:districts,id'],
            'sub_counties_id' => ['required', 'integer', 'exists:sub_counties,id'],
            'regions_id' => ['nullable', 'integer', 'exists:regions,id'],
        ]);

        $parish->name = $request->name;
        $parish->sub_counties_id = $request->sub_counties_id;
        $parish->districts_id = $request->districts_id;
        $parish->regions_id = $request->regions_id;
        $parish->updated_by = Auth::id();
        $parish->save();

        return back()->with('success_message', 'Parish updated successfully');
    }

    public function deleteParish($id)
    {
        $parish = $this->resolveLocationRecord(Parish::class, $id);
        if (!$parish) {
            abort(404, 'Parish record not found.');
        }
        $parish->delete();
        return back()->with('success_message', 'Parish deleted successfully');
    }
}
