<?php

namespace App\Domain\Facility\Services;

use App\Domain\Facility\Models\SpaceLayout;
use App\Domain\Facility\Models\SpaceSegment;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class SpaceLayoutService
{
    /**
     * Create a new space layout with auto-generated segments.
     */
    public function create(array $data, User $creator): SpaceLayout
    {
        return DB::transaction(function () use ($data, $creator) {
            $this->validateNoOverlap($data);

            $layout = SpaceLayout::create([
                ...$data,
                'created_by' => $creator->id,
            ]);

            $this->generateSegments($layout);

            return $layout->load('segments');
        });
    }

    /**
     * Update a layout. Regenerates segments if type/config changed.
     */
    public function update(SpaceLayout $layout, array $data): SpaceLayout
    {
        return DB::transaction(function () use ($layout, $data) {
            $this->validateNoOverlap($data, $layout->id);

            $oldType = $layout->layout_type->value;
            $oldConfig = $layout->layout_config;

            $newType = $data['layout_type'] ?? $oldType;
            $newConfig = $data['layout_config'] ?? $oldConfig;

            $layout->update($data);

            $configChanged = $newType !== $oldType || json_encode($newConfig) !== json_encode($oldConfig);

            if ($configChanged) {
                $layout->segments()->delete();
                $this->generateSegments($layout->fresh());
            }

            return $layout->fresh(['segments']);
        });
    }

    /**
     * Delete a layout and its segments.
     */
    public function delete(SpaceLayout $layout): void
    {
        DB::transaction(function () use ($layout) {
            $layout->segments()->delete();
            $layout->delete();
        });
    }

    /**
     * Resolve which layout applies for a given facility, date, and time.
     * Specific-date layouts override recurring ones.
     */
    public function resolveLayout(int $facilityId, string $date, string $time): ?SpaceLayout
    {
        $dayOfWeek = (int) date('w', strtotime($date));

        // Specific date override takes priority
        $specificLayout = SpaceLayout::where('facility_id', $facilityId)
            ->where('is_recurring', false)
            ->where('effective_date', $date)
            ->where('start_time', '<=', $time)
            ->where('end_time', '>', $time)
            ->where('is_active', true)
            ->first();

        if ($specificLayout) {
            return $specificLayout;
        }

        // Fall back to recurring layout for this day of week
        return SpaceLayout::where('facility_id', $facilityId)
            ->where('is_recurring', true)
            ->where('effective_day_of_week', $dayOfWeek)
            ->where('start_time', '<=', $time)
            ->where('end_time', '>', $time)
            ->where('is_active', true)
            ->first();
    }

    /**
     * Get all available segments for a facility at a given date/time.
     */
    public function getAvailableSegments(int $facilityId, string $date, string $time): array
    {
        $layout = $this->resolveLayout($facilityId, $date, $time);

        if (! $layout) {
            return [];
        }

        return $layout->segments()
            ->where('is_available', true)
            ->orderBy('sort_order')
            ->get()
            ->toArray();
    }

    /**
     * Validate that no overlapping layout exists for the same facility/time slot.
     */
    private function validateNoOverlap(array $data, ?int $excludeId = null): void
    {
        $query = SpaceLayout::where('facility_id', $data['facility_id'])
            ->where('is_active', true)
            ->where('start_time', '<', $data['end_time'])
            ->where('end_time', '>', $data['start_time']);

        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }

        if ($data['is_recurring'] ?? true) {
            $query->where('is_recurring', true)
                  ->where('effective_day_of_week', $data['effective_day_of_week']);
        } else {
            $query->where('is_recurring', false)
                  ->where('effective_date', $data['effective_date']);
        }

        if ($query->exists()) {
            throw new DomainException('يوجد تخطيط متداخل في نفس الفترة الزمنية');
        }
    }

    /**
     * Auto-generate segments based on layout type and config.
     */
    private function generateSegments(SpaceLayout $layout): void
    {
        $type = $layout->layout_type->value;
        $config = $layout->layout_config;

        $segments = match ($type) {
            'grid' => $this->generateGridSegments($config),
            'lanes' => $this->generateLaneSegments($config),
            'zones' => $this->generateZoneSegments($config),
            'custom' => $this->generateCustomSegments($config),
            default => [],
        };

        foreach ($segments as $index => $segment) {
            SpaceSegment::create([
                'space_layout_id' => $layout->id,
                'sort_order' => $index,
                ...$segment,
            ]);
        }
    }

    /**
     * Grid: rows x columns → R1C1, R1C2, R2C1, etc.
     */
    private function generateGridSegments(array $config): array
    {
        $rows = $config['rows'] ?? 2;
        $cols = $config['columns'] ?? 2;
        $segments = [];

        for ($r = 1; $r <= $rows; $r++) {
            for ($c = 1; $c <= $cols; $c++) {
                $code = "R{$r}C{$c}";
                $segments[] = [
                    'code' => $code,
                    'name' => "Row {$r} Col {$c}",
                    'name_ar' => "صف {$r} عمود {$c}",
                    'row_index' => $r,
                    'col_index' => $c,
                ];
            }
        }

        return $segments;
    }

    /**
     * Lanes: lane_count → Lane 1, Lane 2, etc.
     */
    private function generateLaneSegments(array $config): array
    {
        $count = $config['lane_count'] ?? 4;
        $segments = [];

        for ($i = 1; $i <= $count; $i++) {
            $segments[] = [
                'code' => "L{$i}",
                'name' => "Lane {$i}",
                'name_ar' => "حارة {$i}",
                'lane_number' => $i,
            ];
        }

        return $segments;
    }

    /**
     * Zones: definitions array → one segment per zone definition.
     */
    private function generateZoneSegments(array $config): array
    {
        $definitions = $config['definitions'] ?? [];
        $segments = [];

        foreach ($definitions as $index => $def) {
            $segments[] = [
                'code' => $def['code'] ?? 'Z' . ($index + 1),
                'name' => $def['name'] ?? 'Zone ' . ($index + 1),
                'name_ar' => $def['name_ar'] ?? 'منطقة ' . ($index + 1),
                'area_sqm' => $def['area_sqm'] ?? null,
                'capacity' => $def['capacity'] ?? null,
            ];
        }

        return $segments;
    }

    /**
     * Custom: same as zones (no geometric constraints).
     */
    private function generateCustomSegments(array $config): array
    {
        return $this->generateZoneSegments($config);
    }
}
