<?php

namespace App\Http\Controllers\api\SpotPlantation;

use App\Http\Controllers\Controller;
use App\Models\Road;
use App\Models\SpotPlantation;
use App\Models\PlantationOfficerWard;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

/**
 * Spot Unplanned Plantation
 * ---------------------------------------------------------------------------
 * Roads : shared per-ward master. Any officer WITH ACCESS to the ward may add.
 * Entry : one row per road-spot (JE only), photos[] grows per photo-click.
 * Photo : each append needs mandatory latitude/longitude; stored in the
 *         dedicated folder  storage/app/public/spot_plantations
 * Report: date / ward / zone filters, always bounded by the viewer's scope,
 *         with by_zone / by_ward / by_date rollups + total_entries + total_photos.
 *
 * Scope convention (matches existing working controllers):
 *   role 1                    = Commissioner / Super admin -> all
 *   role 7                    = Chief Engineer             -> all
 *   role 2 + zone_id NULL     = Nagar Aayukt               -> all
 *   role 2 + zone_id set      = Zonal Officer              -> own zone
 *   role 8 (XEN)/9 (AE)       = subtree (their JEs' wards)
 *   role 10 (JE)              = own allotted wards (only role that captures)
 */
class SpotPlantationController extends Controller
{
    private const ROLE_XEN = 8;
    private const ROLE_AE   = 9;
    private const ROLE_JE   = 10;
    private const SEE_ALL_ROLE_IDS = [1, 7];
    private const PHOTO_DIR = 'spot_plantations';

    /* =====================================================================
     * SCOPE HELPERS
     * ===================================================================== */

    /**
     * Returns ['all' => true] if the user sees everything, otherwise
     * ['ward_ids' => [...]] or ['zone_id' => n] describing their access.
     */
    private function accessScope($user): array
    {
        $roleId = (int) $user->role_id;

        if (in_array($roleId, self::SEE_ALL_ROLE_IDS, true)) {
            return ['all' => true];
        }
        if ($roleId === 2 && empty($user->zone_id)) {
            return ['all' => true];                       // Nagar Aayukt
        }
        if ($roleId === 2 && !empty($user->zone_id)) {
            return ['zone_id' => (int) $user->zone_id];   // Zonal
        }
        if ($roleId === self::ROLE_XEN) {
            return ['ward_ids' => PlantationOfficerWard::where('xen_id', $user->id)
                ->where('status', 1)->pluck('ward_id')->all()];
        }
        if ($roleId === self::ROLE_AE) {
            return ['ward_ids' => PlantationOfficerWard::where('ae_id', $user->id)
                ->where('status', 1)->pluck('ward_id')->all()];
        }
        if ($roleId === self::ROLE_JE) {
            return ['ward_ids' => PlantationOfficerWard::where('je_id', $user->id)
                ->where('status', 1)->pluck('ward_id')->all()];
        }
        return ['ward_ids' => []];                        // any other role -> nothing
    }

    /** Can this user access a specific ward? */
    private function canAccessWard($user, int $wardId): bool
    {
        $scope = $this->accessScope($user);
        if (!empty($scope['all'])) {
            return true;
        }
        if (isset($scope['zone_id'])) {
            return DB::table('wards')->where('id', $wardId)
                ->where('zone_id', $scope['zone_id'])->exists();
        }
        return in_array($wardId, $scope['ward_ids'] ?? [], true);
    }

    /** Apply the user's scope to a spot_plantations query builder. */
    private function applyScope($query, $user)
    {
        $scope = $this->accessScope($user);
        if (!empty($scope['all'])) {
            return $query;
        }
        if (isset($scope['zone_id'])) {
            return $query->where('zone_id', $scope['zone_id']);
        }
        return $query->whereIn('ward_id', $scope['ward_ids'] ?? [-1]);
    }

    /* =====================================================================
     * ROADS  (add = anyone with ward access; list = anyone with ward access)
     * ===================================================================== */

    public function addRoad(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'ward_id'   => 'required|integer|exists:wards,id',
            'road_name' => 'required|string|max:255',
        ]);
        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => $validator->errors()->first(), 'errors' => $validator->errors()], 422);
        }

        $user = auth()->user();

        if (!$this->canAccessWard($user, (int) $request->ward_id)) {
            return response()->json(['status' => false, 'message' => 'You do not have access to this ward.'], 403);
        }

        // derive zone from ward
        $ward = DB::table('wards')->where('id', $request->ward_id)->first();

        // prevent duplicate road name in same ward
        $exists = Road::where('ward_id', $request->ward_id)
            ->where('road_name', $request->road_name)->first();
        if ($exists) {
            return response()->json([
                'status'  => true,
                'message' => 'Road already exists.',
                'data'    => ['id' => $exists->id, 'road_name' => $exists->road_name],
            ], 200);
        }

        $road = Road::create([
            'ward_id'    => $request->ward_id,
            'zone_id'    => $ward->zone_id,
            'road_name'  => $request->road_name,
            'created_by' => $user->id,
        ]);

        return response()->json([
            'status'  => true,
            'message' => 'Road added successfully.',
            'data'    => ['id' => $road->id, 'road_name' => $road->road_name],
        ], 201);
    }

    public function listRoads(Request $request, $ward_id)
    {
        $user = auth()->user();
        if (!$this->canAccessWard($user, (int) $ward_id)) {
            return response()->json(['status' => false, 'message' => 'You do not have access to this ward.'], 403);
        }

        $roads = Road::with('creator')
            ->where('ward_id', $ward_id)->where('status', 1)
            ->orderBy('road_name')
            ->get()
            ->map(fn($r) => [
                'id'         => $r->id,
                'road_name'  => $r->road_name,
                'added_by'   => optional($r->creator)->name,
                'entries'    => $r->entries()->count(),
            ]);

        return response()->json(['status' => true, 'data' => $roads], 200);
    }

    /* =====================================================================
     * PLANTATION ENTRY  (JE only, own wards)
     * ===================================================================== */

    public function createEntry(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'road_id' => 'required|integer|exists:roads,id',
            'remarks' => 'nullable|string',
        ]);
        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => $validator->errors()->first(), 'errors' => $validator->errors()], 422);
        }

        $user = auth()->user();
        if ((int) $user->role_id !== self::ROLE_JE) {
            return response()->json(['status' => false, 'message' => 'Only JE can add plantation entries.'], 403);
        }

        $road = Road::find($request->road_id);

        // JE must own the road's ward
        $ownsWard = PlantationOfficerWard::where('je_id', $user->id)
            ->where('ward_id', $road->ward_id)->where('status', 1)->exists();
        if (!$ownsWard) {
            return response()->json(['status' => false, 'message' => 'This ward is not allotted to you.'], 403);
        }

        $entry = SpotPlantation::create([
            'road_id'    => $road->id,
            'ward_id'    => $road->ward_id,
            'zone_id'    => $road->zone_id,
            'photos'     => [],
            'remarks'    => $request->remarks,
            'created_by' => $user->id,
        ]);

        return response()->json([
            'status'  => true,
            'message' => 'Entry created. You can now add photos.',
            'data'    => ['id' => $entry->id],
        ], 201);
    }

    /**
     * Append ONE geo-tagged photo to an entry. Called on every photo-click so
     * nothing is missed. Latitude & longitude are MANDATORY.
     */
    public function addPhoto(Request $request, $id)
    {
        $validator = Validator::make($request->all(), [
            'image'     => 'required|image|mimes:jpg,jpeg,png|max:5120',
            'latitude'  => 'required|numeric',
            'longitude' => 'required|numeric',
        ]);
        if ($validator->fails()) {
            return response()->json(['status' => false, 'message' => $validator->errors()->first(), 'errors' => $validator->errors()], 422);
        }

        $entry = SpotPlantation::find($id);
        if (!$entry) {
            return response()->json(['status' => false, 'message' => 'Entry not found.'], 404);
        }

        // only the JE who created it may add photos
        if ($entry->created_by !== auth()->id()) {
            return response()->json(['status' => false, 'message' => 'Not allowed.'], 403);
        }

        DB::beginTransaction();
        try {
            // store in the dedicated folder
            $path = Storage::disk('public')->putFile(self::PHOTO_DIR, $request->file('image'));

            $photos   = $entry->photos ?? [];
            $photos[] = [
                'path'      => $path,
                'latitude'  => $request->latitude,
                'longitude' => $request->longitude,
                'at'        => now()->toDateTimeString(),
            ];
            $entry->photos = $photos;
            $entry->save();

            DB::commit();

            return response()->json([
                'status'  => true,
                'message' => 'Photo saved.',
                'data'    => ['total_photos' => count($photos)],
            ], 201);
        } catch (\Exception $e) {
            DB::rollBack();
            \Log::error($e);
            return response()->json(['status' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /** List entries (with photos) for a road — scoped. */
    public function roadEntries(Request $request, $road_id)
    {
        $user = auth()->user();
        $query = SpotPlantation::with(['road', 'getWard', 'zone', 'creator'])
            ->where('road_id', $road_id)->where('status', 1);
        $query = $this->applyScope($query, $user);

        $rows = $query->orderByDesc('id')->get()->map(fn($r) => $this->transform($r));
        return response()->json(['status' => true, 'data' => $rows], 200);
    }

    public function show($id)
    {
        $entry = SpotPlantation::with(['road', 'getWard', 'zone', 'creator'])->find($id);
        if (!$entry) {
            return response()->json(['status' => false, 'message' => 'Not found.'], 404);
        }
        if (!$this->canAccessWard(auth()->user(), (int) $entry->ward_id)) {
            return response()->json(['status' => false, 'message' => 'Not allowed.'], 403);
        }
        return response()->json(['status' => true, 'data' => $this->transform($entry)], 200);
    }

    /* =====================================================================
     * REPORT  (date / ward / zone filters, bounded by scope, with rollups)
     * ===================================================================== */

    public function report(Request $request)
    {
        $user  = auth()->user();
        $query = SpotPlantation::with(['road', 'getWard', 'zone', 'creator'])->where('status', 1);

        // 1) hard scope bound
        $query = $this->applyScope($query, $user);
        $scope = $this->accessScope($user);
        $scopeLabel = !empty($scope['all']) ? 'all_zones'
            : (isset($scope['zone_id']) ? 'own_zone' : 'own_wards');

        // 2) optional filters (narrow within scope; can't widen it)
        if ($request->filled('ward_id')) {
            $query->where('ward_id', (int) $request->ward_id);
        }
        if ($request->filled('zone_id')) {
            $query->where('zone_id', (int) $request->zone_id);
        }
        if ($request->filled('date')) {
            $query->whereDate('created_at', $request->date);
        }
        if ($request->filled('from_date')) {
            $query->whereDate('created_at', '>=', $request->from_date);
        }
        if ($request->filled('to_date')) {
            $query->whereDate('created_at', '<=', $request->to_date);
        }

        $rows = $query->orderByDesc('id')->get();

        // photo count helper
        $photoCount = fn($r) => is_array($r->photos) ? count($r->photos) : 0;
        $totalPhotos = $rows->sum($photoCount);

        // 3) rollups
        $byZone = $rows->groupBy('zone_id')->map(function ($g) use ($photoCount) {
            $first = $g->first();
            return [
                'zone_id'   => $first->zone_id,
                'zone_name' => optional($first->zone)->zone_name,
                'entries'   => $g->count(),
                'photos'    => $g->sum($photoCount),
            ];
        })->values();

        $byWard = $rows->groupBy('ward_id')->map(function ($g) use ($photoCount) {
            $first = $g->first();
            return [
                'ward_id'     => $first->ward_id,
                'ward_number' => optional($first->getWard)->ward_number,
                'ward_name'   => optional($first->getWard)->ward_name,
                'entries'     => $g->count(),
                'photos'      => $g->sum($photoCount),
            ];
        })->values();

        $byDate = $rows->groupBy(fn($r) => optional($r->created_at)->toDateString())
            ->map(fn($g, $d) => [
                'date'    => $d,
                'entries' => $g->count(),
                'photos'  => $g->sum($photoCount),
            ])->values();

        return response()->json([
            'status'  => true,
            'summary' => [
                'scope'         => $scopeLabel,
                'total_entries' => $rows->count(),
                'total_photos'  => $totalPhotos,
                'filters'       => [
                    'date'      => $request->date,
                    'from_date' => $request->from_date,
                    'to_date'   => $request->to_date,
                    'ward_id'   => $request->ward_id,
                    'zone_id'   => $request->zone_id,
                ],
            ],
            'by_zone' => $byZone,
            'by_ward' => $byWard,
            'by_date' => $byDate,
            'data'    => $rows->map(fn($r) => $this->transform($r)),
        ], 200);
    }

    /* =====================================================================
     * helper
     * ===================================================================== */

    private function transform(SpotPlantation $r): array
    {
        return [
            'id'           => $r->id,
            'road_id'      => $r->road_id,
            'road_name'    => optional($r->road)->road_name,
            'zone_id'      => $r->zone_id,
            'zone_name'    => optional($r->zone)->zone_name,
            'ward_id'      => $r->ward_id,
            'ward_number'  => optional($r->getWard)->ward_number,
            'ward_name'    => optional($r->getWard)->ward_name,
            'photos'       => collect($r->photos ?? [])->map(function ($p) {
                $p['url'] = isset($p['path']) ? Storage::disk('public')->url($p['path']) : null;
                return $p;
            }),
            'photo_count'  => is_array($r->photos) ? count($r->photos) : 0,
            'remarks'      => $r->remarks,
            'created_by'   => optional($r->creator)->name,
            'created_at'   => optional($r->created_at)->toDateTimeString(),
        ];
    }
}
