<?php

class LiquidityService
{
    public static function analyze(array $candles, array $swings, string $bias): array
    {
        $buySide = self::getBuySideLiquidity($swings, $candles);
        $sellSide = self::getSellSideLiquidity($swings, $candles);
        $equalHighs = self::detectEqualHighs($swings);
        $equalLows = self::detectEqualLows($swings);
        $sweep = self::detectLiquiditySweep($candles, $swings);
        $inducement = self::detectInducement($candles, $bias);

        return [
            'buy_side' => $buySide,
            'sell_side' => $sellSide,
            'equal_highs' => $equalHighs,
            'equal_lows' => $equalLows,
            'sweep' => $sweep,
            'inducement' => $inducement,
            'draw_on_liquidity' => self::getDrawOnLiquidity($bias, $buySide, $sellSide),
            'summary' => self::buildSummary($bias, $buySide, $sellSide, $sweep, $inducement)
        ];
    }

    public static function getBuySideLiquidity(array $swings, array $candles = []): ?float
    {
        $highs = array_values(array_filter($swings, fn($s) => ($s['type'] ?? '') === 'HIGH'));

        if (!empty($highs)) {
            return (float)end($highs)['price'];
        }

        if (!empty($candles)) {
            $recent = array_slice($candles, -60);
            return max(array_map(fn($c) => (float)$c['high_price'], $recent));
        }

        return null;
    }

    public static function getSellSideLiquidity(array $swings, array $candles = []): ?float
    {
        $lows = array_values(array_filter($swings, fn($s) => ($s['type'] ?? '') === 'LOW'));

        if (!empty($lows)) {
            return (float)end($lows)['price'];
        }

        if (!empty($candles)) {
            $recent = array_slice($candles, -60);
            return min(array_map(fn($c) => (float)$c['low_price'], $recent));
        }

        return null;
    }

    public static function detectEqualHighs(array $swings, float $tolerance = 0.0005): array
    {
        $highs = array_values(array_filter($swings, fn($s) => ($s['type'] ?? '') === 'HIGH'));
        $zones = [];

        for ($i = 1; $i < count($highs); $i++) {
            $a = (float)$highs[$i - 1]['price'];
            $b = (float)$highs[$i]['price'];

            if (abs($a - $b) <= $tolerance) {
                $zones[] = [
                    'type' => 'EQUAL_HIGHS',
                    'price' => round(($a + $b) / 2, 5),
                    'from_time' => $highs[$i - 1]['time'] ?? null,
                    'to_time' => $highs[$i]['time'] ?? null,
                    'narrative' => 'Buy-side liquidity resting above equal highs.'
                ];
            }
        }

        return $zones;
    }

    public static function detectEqualLows(array $swings, float $tolerance = 0.0005): array
    {
        $lows = array_values(array_filter($swings, fn($s) => ($s['type'] ?? '') === 'LOW'));
        $zones = [];

        for ($i = 1; $i < count($lows); $i++) {
            $a = (float)$lows[$i - 1]['price'];
            $b = (float)$lows[$i]['price'];

            if (abs($a - $b) <= $tolerance) {
                $zones[] = [
                    'type' => 'EQUAL_LOWS',
                    'price' => round(($a + $b) / 2, 5),
                    'from_time' => $lows[$i - 1]['time'] ?? null,
                    'to_time' => $lows[$i]['time'] ?? null,
                    'narrative' => 'Sell-side liquidity resting below equal lows.'
                ];
            }
        }

        return $zones;
    }

    public static function detectLiquiditySweep(array $candles, array $swings): array
    {
        if (count($candles) < 5 || count($swings) < 2) {
            return [
                'detected' => false,
                'type' => null,
                'level' => null,
                'time' => null,
                'narrative' => 'No liquidity sweep detected.'
            ];
        }

        $last = end($candles);
        $lastHigh = (float)$last['high_price'];
        $lastLow = (float)$last['low_price'];
        $lastClose = (float)$last['close_price'];

        $buySide = self::getBuySideLiquidity($swings);
        $sellSide = self::getSellSideLiquidity($swings);

        if ($buySide !== null && $lastHigh > $buySide && $lastClose < $buySide) {
            return [
                'detected' => true,
                'type' => 'BUY_SIDE_SWEEP',
                'level' => $buySide,
                'time' => $last['open_time'] ?? null,
                'narrative' => 'Buy-side liquidity was swept and price closed back below the level.'
            ];
        }

        if ($sellSide !== null && $lastLow < $sellSide && $lastClose > $sellSide) {
            return [
                'detected' => true,
                'type' => 'SELL_SIDE_SWEEP',
                'level' => $sellSide,
                'time' => $last['open_time'] ?? null,
                'narrative' => 'Sell-side liquidity was swept and price closed back above the level.'
            ];
        }

        return [
            'detected' => false,
            'type' => null,
            'level' => null,
            'time' => null,
            'narrative' => 'No liquidity sweep detected.'
        ];
    }

    public static function detectInducement(array $candles, string $bias): array
    {
        if (count($candles) < 10) {
            return [
                'detected' => false,
                'level' => null,
                'type' => null,
                'narrative' => 'Not enough candles to detect inducement.'
            ];
        }

        $recent = array_slice($candles, -20);

        if ($bias === 'BUY') {
            $minorLow = min(array_map(fn($c) => (float)$c['low_price'], array_slice($recent, -8)));

            return [
                'detected' => true,
                'level' => $minorLow,
                'type' => 'BUY_INDUCEMENT',
                'narrative' => 'Possible inducement below recent minor lows before bullish continuation.'
            ];
        }

        if ($bias === 'SELL') {
            $minorHigh = max(array_map(fn($c) => (float)$c['high_price'], array_slice($recent, -8)));

            return [
                'detected' => true,
                'level' => $minorHigh,
                'type' => 'SELL_INDUCEMENT',
                'narrative' => 'Possible inducement above recent minor highs before bearish continuation.'
            ];
        }

        return [
            'detected' => false,
            'level' => null,
            'type' => null,
            'narrative' => 'No clear inducement direction.'
        ];
    }

    public static function getDrawOnLiquidity(string $bias, ?float $buySide, ?float $sellSide): array
    {
        if ($bias === 'BUY') {
            return [
                'side' => 'BUY SIDE',
                'target' => $buySide,
                'narrative' => 'Primary draw is likely toward buy-side liquidity.'
            ];
        }

        if ($bias === 'SELL') {
            return [
                'side' => 'SELL SIDE',
                'target' => $sellSide,
                'narrative' => 'Primary draw is likely toward sell-side liquidity.'
            ];
        }

        return [
            'side' => 'UNKNOWN',
            'target' => null,
            'narrative' => 'No clear draw on liquidity.'
        ];
    }

    private static function buildSummary(string $bias, ?float $buySide, ?float $sellSide, array $sweep, array $inducement): string
    {
        if (!empty($sweep['detected'])) {
            return $sweep['narrative'];
        }

        if (!empty($inducement['detected'])) {
            return $inducement['narrative'];
        }

        if ($bias === 'BUY' && $buySide !== null) {
            return 'Market may seek buy-side liquidity above recent highs.';
        }

        if ($bias === 'SELL' && $sellSide !== null) {
            return 'Market may seek sell-side liquidity below recent lows.';
        }

        return 'Liquidity conditions are neutral.';
    }
}