<?php

namespace App\Services\FishScore;

use App\Services\FishScore\Contracts\CoastlineServiceInterface;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class PostgisCoastlineService implements CoastlineServiceInterface
{
    public function isOperational(): bool
    {
        if (Schema::getConnection()->getDriverName() !== 'pgsql') {
            return false;
        }

        return Schema::hasTable('fish_land_polygons')
            && DB::table('fish_land_polygons')->exists();
    }

    public function isSea(float $lat, float $lon): ?bool
    {
        if (! $this->withinStudyBbox($lat, $lon)) {
            return config('fish.outside_bbox_treat_as_land') ? false : null;
        }

        if (! $this->isOperational()) {
            return null;
        }

        $row = DB::selectOne(
            'SELECT EXISTS (
                SELECT 1 FROM fish_land_polygons
                WHERE ST_Covers(geom, ST_SetSRID(ST_MakePoint(?, ?), 4326))
            ) AS on_land',
            [$lon, $lat]
        );

        return ! ((bool) ($row->on_land ?? false));
    }

    public function distanceToCoastlineMeters(float $lat, float $lon): ?float
    {
        if (! $this->isOperational()) {
            return null;
        }

        $expand = (float) config('fish.coast_query_expand_deg', 1.5);
        $lon = (float) $lon;
        $lat = (float) $lat;

        $row = DB::selectOne(
            'SELECT MIN(
                ST_Distance(
                    ST_SetSRID(ST_MakePoint(?, ?), 4326)::geography,
                    ST_Boundary(geom)::geography
                )
            ) AS d
            FROM fish_land_polygons
            WHERE geom && ST_Expand(ST_SetSRID(ST_MakePoint(?, ?), 4326), ?)',
            [$lon, $lat, $lon, $lat, $expand]
        );

        if (($row->d ?? null) === null) {
            $row = DB::selectOne(
                'SELECT MIN(
                    ST_Distance(
                        ST_SetSRID(ST_MakePoint(?, ?), 4326)::geography,
                        ST_Boundary(geom)::geography
                    )
                ) AS d
                FROM fish_land_polygons',
                [$lon, $lat]
            );
        }

        return isset($row->d) ? round((float) $row->d, 1) : null;
    }

    private function withinStudyBbox(float $lat, float $lon): bool
    {
        $b = config('fish.study_bbox');

        return $lat >= $b['min_lat'] && $lat <= $b['max_lat']
            && $lon >= $b['min_lon'] && $lon <= $b['max_lon'];
    }
}
