import { _decorator, Vec3, Node, math } from "cc";
import { Snake } from "../Snake";
import { AIBehaviorParams } from "./AIBehaviorParams";
import { AISnake } from "../AISnake";
import { MainGame } from "../MainGame";
import { Global } from "../Global";
import { FoodManger } from "../Manager/FoodManger";

const { ccclass, property } = _decorator;

export interface PerceptionResult {
    snake: Snake;
    nearestFood: Vec3 | null;
    nearestThreat: Snake | null;
    nearbyCompetitors: Snake[];
    boundaryDistance: number;
    player: Snake | null;
    assistPartner: AISnake | null;
    dangerLevel: number;
    territoryControl: number;
    powerRatio: number;
}

@ccclass("AIPerception")
export class AIPerception {

    private readonly SAFE_MARGIN = 4.0;
    private readonly THREAT_DETECTION_RANGE = 500;
    private readonly FOOD_EVALUATION_RANGE = 600;
    private readonly TERRITORY_INFLUENCE_RANGE = 400;

    constructor(
        private snake: Snake,
        private params: AIBehaviorParams
    ) { }

    analyze(): PerceptionResult {
        const myPos = this.snake.head.getPosition();
        const player = MainGame.ins.player;
        const situationAnalysis = this.analyzeSituation(myPos);

        return {
            snake: this.snake,
            nearestFood: this.findOptimalFood(situationAnalysis),
            nearestThreat: this.findPrimaryThreat(situationAnalysis),
            nearbyCompetitors: this.findRelevantCompetitors(situationAnalysis),
            boundaryDistance: this.calculateBoundaryRisk(myPos),
            player: player,
            assistPartner: this.findBestAssistPartner(player, situationAnalysis),
            dangerLevel: situationAnalysis.threatLevel,
            territoryControl: situationAnalysis.territoryControl,
            powerRatio: situationAnalysis.powerRatio
        };
    }

    private analyzeSituation(myPos: Vec3) {
        const myLength = this.snake.length;
        let territoryControl = 0;
        let threatLevel = 0;
        let powerRatio = 1;

        const competitors = this.findNearbyCompetitors();
        const threats: Snake[] = [];

        for (const competitor of competitors) {
            const distance = Vec3.distance(myPos, competitor.head.getPosition());
            const competitorLength = competitor.length;
            const strengthRatio = myLength / competitorLength;
            
            // 计算影响力衰减
            const influence = 1 / (1 + Math.pow(distance / this.TERRITORY_INFLUENCE_RANGE, 2));
            
            // 评估威胁和领域控制
            if (strengthRatio < 1.2) { // 略微提高威胁判定阈值
                const threatScore = influence * (1.2 - strengthRatio);
                threatLevel = Math.max(threatLevel, threatScore);
                if (distance < this.THREAT_DETECTION_RANGE) {
                    threats.push(competitor);
                }
            } else {
                territoryControl += influence * (strengthRatio - 1);
            }
        }

        // 计算整体实力比
        const totalLength = competitors.reduce((sum, snake) => sum + snake.length, 0);
        powerRatio = totalLength > 0 ? myLength / (totalLength / competitors.length) : 2;

        // 考虑边界威胁
        const boundaryRisk = this.calculateBoundaryRisk(myPos);
        threatLevel = Math.max(threatLevel, boundaryRisk);

        return {
            threatLevel: Math.min(1, threatLevel),
            territoryControl: Math.min(1, territoryControl),
            powerRatio,
            competitors,
            threats
        };
    }


    private findOptimalFood(situation: any): Vec3 | null {
        const foods = FoodManger.ins.node.children;
        let bestFood = null;
        let bestScore = -Infinity;
        const myPos = this.snake.head.getPosition();

        for (const food of foods) {
            if (!food.isValid || !food.active) continue;
            
            const foodPos = food.getPosition();
            const distance = Vec3.distance(myPos, foodPos);
            
            if (distance > this.FOOD_EVALUATION_RANGE) continue;

            const score = this.evaluateFoodValue(foodPos, distance, situation);
            if (score > bestScore) {
                bestScore = score;
                bestFood = foodPos;
            }
        }

        return bestFood;
    }

    private evaluateFoodValue(foodPos: Vec3, distance: number, situation: any): number {
        let score = 1000 - distance; // 基础分数

        // 竞争者距离惩罚
        for (const competitor of situation.competitors) {
            const competitorDist = Vec3.distance(competitor.head.getPosition(), foodPos);
            if (competitorDist < distance) {
                score *= 0.7;
            }
        }

        // 威胁距离惩罚
        for (const threat of situation.threats) {
            const threatDist = Vec3.distance(threat.head.getPosition(), foodPos);
            if (threatDist < this.SAFE_MARGIN * 2) {
                score *= 0.5;
            }
        }

        // 边界安全性评估
        score *= this.evaluatePositionSafety(foodPos);

        // 根据整体局势调整
        score *= (1 - situation.threatLevel * 0.7);
        score *= (1 + situation.territoryControl * 0.3);

        return score;
    }


    private findPrimaryThreat(situation: any): Snake | null {
        if (situation.threats.length === 0) return null;

        const myPos = this.snake.head.getPosition();
        let primaryThreat = null;
        let highestThreatScore = 0;

        for (const threat of situation.threats) {
            const distance = Vec3.distance(myPos, threat.head.getPosition());
            const lengthRatio = threat.length / this.snake.length;
            const threatScore = lengthRatio * (1 - distance / this.THREAT_DETECTION_RANGE);

            if (threatScore > highestThreatScore) {
                highestThreatScore = threatScore;
                primaryThreat = threat;
            }
        }

        return primaryThreat;
    }

    private findRelevantCompetitors(situation: any): Snake[] {
        return situation.competitors.filter(competitor => {
            const distance = Vec3.distance(
                this.snake.head.getPosition(),
                competitor.head.getPosition()
            );
            return distance <= this.params.params.viewDistance;
        });
    }

    private calculateBoundaryRisk(position: Vec3): number {
        const mapHalfWidth = Global.HALF_MAP_WIDTH;
        const mapHalfHeight = Global.HALF_MAP_HEIGHT;
        
        const distanceToEdge = Math.min(
            mapHalfWidth - Math.abs(position.x),
            mapHalfHeight - Math.abs(position.y)
        );
        
        return Math.max(0, 1 - distanceToEdge / (this.SAFE_MARGIN * 2));
    }

    private evaluatePositionSafety(pos: Vec3): number {
        const mapHalfWidth = Global.HALF_MAP_WIDTH;
        const mapHalfHeight = Global.HALF_MAP_HEIGHT;
        
        const distToBoundaryX = Math.min(mapHalfWidth - Math.abs(pos.x), mapHalfWidth);
        const distToBoundaryY = Math.min(mapHalfHeight - Math.abs(pos.y), mapHalfHeight);
        
        return Math.min(
            1,
            (distToBoundaryX / (mapHalfWidth * 0.3)) *
            (distToBoundaryY / (mapHalfHeight * 0.3))
        );
    }

    private findBestAssistPartner(player: Snake, situation: any): AISnake | null {
        if (!player?.isLife || situation.threatLevel > 0.6) return null;

        const myPos = this.snake.head.getPosition();
        const myLength = this.snake.length;
        let bestPartner: AISnake = null;
        let bestScore = 0;

        for (const competitor of situation.competitors) {
            if (!(competitor instanceof AISnake)) continue;

            const distance = Vec3.distance(myPos, competitor.head.getPosition());
            if (distance > this.params.params.viewDistance * 0.7) continue;

            const combinedLength = myLength + competitor.length;
            const playerLength = player.length;
            
            if (combinedLength > playerLength * 1.3) {
                const score = this.evaluateAssistPartner(competitor, player, distance);
                if (score > bestScore) {
                    bestScore = score;
                    bestPartner = competitor;
                }
            }
        }

        return bestPartner;
    }

    private evaluateAssistPartner(partner: AISnake, target: Snake, distance: number): number {
        const partnerLength = partner.length;
        const targetLength = target.length;
        const myLength = this.snake.length;

        let score = (partnerLength + myLength) / targetLength;
        score *= (1 - distance / this.params.params.viewDistance);
        score *= partner.isLife ? 1 : 0;

        return score;
    }
    

    private findNearestThreat(): Snake | null {
        const myPos = this.snake.head.getPosition();
        const myLength = this.snake.length;
        let nearestThreat = null;
        let minDistance = this.params.params.viewDistance;

        const allSnakes = [...MainGame.ins.animalNode.children, MainGame.ins.player.node];

        for (const snakeNode of allSnakes) {
            const snake = snakeNode.getComponent(Snake);
            if (snake === this.snake || !snake?.isLife) continue;

            // 增加长度检查的阈值，更容易将其他蛇视为威胁
            if (snake.length > myLength * 0.8) {
                const headDistance = Vec3.distance(myPos, snake.head.getPosition());
                if (headDistance < minDistance) {
                    minDistance = headDistance;
                    nearestThreat = snake;
                }
            }

            // 检查身体碰撞的威胁
            for (const bodyPart of snake.bodyArr) {
                const bodyDistance = Vec3.distance(myPos, bodyPart.getPosition());
                if (bodyDistance < this.SAFE_MARGIN * 2) {
                    minDistance = bodyDistance;
                    nearestThreat = snake;
                    break;
                }
            }
        }

        return nearestThreat;
    }

    private findNearbyCompetitors(): Snake[] {
        const myPos = this.snake.head.getPosition();
        const competitors: Snake[] = [];
        const allSnakes = [...MainGame.ins.animalNode.children, MainGame.ins.player.node];

        for (const snakeNode of allSnakes) {
            const snake = snakeNode.getComponent(Snake);
            if (snake === this.snake || !snake?.isLife) continue;

            const distance = Vec3.distance(myPos, snake.head.getPosition());
            if (distance <= this.params.params.viewDistance) {
                competitors.push(snake);
            }
        }

        return competitors;
    }

    private getBoundaryDistance(position: Vec3): number {
        const mapHalfWidth = Global.HALF_MAP_WIDTH;
        const mapHalfHeight = Global.HALF_MAP_HEIGHT;

        const distanceToRight = mapHalfWidth - position.x;
        const distanceToLeft = position.x + mapHalfWidth;
        const distanceToTop = mapHalfHeight - position.y;
        const distanceToBottom = position.y + mapHalfHeight;

        return Math.min(distanceToRight, distanceToLeft, distanceToTop, distanceToBottom);
    }


    // private findAssistPartner(player: Snake): AISnake | null {
    //     if (!player?.isLife) return null;

    //     const myPos = this.snake.head.getPosition();
    //     const playerPos = player.head.getPosition();
    //     let bestPartner: AISnake = null;
    //     let bestScore = -1;

    //     for (const node of MainGame.ins.animalNode.children) {
    //         const otherAI = node.getComponent(AISnake);
    //         if (!otherAI?.isLife || otherAI === this.snake) continue;

    //         const distanceToAI = Vec3.distance(myPos, otherAI.head.getPosition());
    //         const distanceToPlayer = Vec3.distance(otherAI.head.getPosition(), playerPos);

    //         if (distanceToAI < this.params.ASSIST_DISTANCE &&
    //             distanceToPlayer < this.params.ASSIST_DISTANCE) {
    //             const score = this.calculateAssistScore(otherAI, player);
    //             if (score > bestScore) {
    //                 bestScore = score;
    //                 bestPartner = otherAI;
    //             }
    //         }
    //     }

    //     return bestPartner;
    // }

    private findAssistPartner(player: Snake): AISnake | null {
        if (!player?.isLife) return null;

        const myPos = this.snake.head.getPosition();
        const myLength = this.snake.length;
        let bestPartner: AISnake = null;
        let bestScore = 0;

        for (const snakeNode of MainGame.ins.animalNode.children) {
            const partner = snakeNode.getComponent(AISnake);
            if (!partner?.isLife || partner === this.snake) continue;

            const distance = Vec3.distance(myPos, partner.head.getPosition());
            if (distance > this.params.params.viewDistance) continue;

            const score = this.calculateAssistScore(partner, player);
            if (score > bestScore) {
                bestScore = score;
                bestPartner = partner;
            }
        }

        return bestPartner;
    }

    private calculateDangerLevel(position: Vec3): number {
        let dangerLevel = 0;

        // 边界危险度
        const boundaryDistance = this.getBoundaryDistance(position);
        if (boundaryDistance < this.params.ESCAPE_BOUNDARY) {
            dangerLevel += 1 - (boundaryDistance / this.params.ESCAPE_BOUNDARY);
        }

        // 威胁危险度
        const threat = this.findNearestThreat();
        if (threat) {
            const distance = Vec3.distance(position, threat.head.getPosition());
            const threatFactor = 1 - (distance / this.params.params.viewDistance);
            dangerLevel += threatFactor * (threat.length / this.snake.length);
        }

        return math.clamp(dangerLevel, 0, 1);
    }

    // 辅助方法
    private calculateAssistScore(partner: AISnake, target: Snake): number {
        const partnerLength = partner.length;
        const targetLength = target.length;
        const myLength = this.snake.length;

        return (partnerLength + myLength) / targetLength;
    }

    private findAlternativeFood(foods: Node[], myPos: Vec3): Vec3 | null {
        let bestAlternative = null;
        let bestScore = -1;

        for (const food of foods) {
            if (!food.isValid || !food.active) continue;

            const foodPos = food.getPosition();
            const distance = Vec3.distance(myPos, foodPos);

            if (distance > this.params.params.viewDistance) continue;

            const competitors = this.findCompetitorsForFood(foodPos, distance);
            const score = this.calculateFoodScore(foodPos, distance, competitors);

            if (score > bestScore) {
                bestScore = score;
                bestAlternative = foodPos;
            }
        }

        return bestAlternative;
    }

    private calculateFoodScore(foodPos: Vec3, distance: number, competitors: Snake[]): number {
        let score = 1 - (distance / this.params.params.viewDistance);

        for (const competitor of competitors) {
            const competitorDistance = Vec3.distance(competitor.head.getPosition(), foodPos);
            if (competitorDistance < distance) {
                score *= 0.5;
            }
        }

        score *= math.lerp(0.8, 1.2, math.random() * this.params.params.predictionAccuracy);
        return score;
    }

    private findCompetitorsForFood(foodPos: Vec3, myDistance: number): Snake[] {
        return this.findNearbyCompetitors().filter(competitor => {
            const distance = Vec3.distance(competitor.head.getPosition(), foodPos);
            return distance <= myDistance * 1.2;
        });
    }

    private canReachFoodFirst(foodPos: Vec3, myDistance: number, competitors: Snake[]): boolean {
        const mySpeed = this.snake.speed * this.snake.moveScale;
        const myTimeToReach = myDistance / mySpeed;

        return !competitors.some(competitor => {
            const competitorDistance = Vec3.distance(competitor.head.getPosition(), foodPos);
            const competitorSpeed = competitor.speed * (competitor instanceof AISnake ? 2 : 1);
            return (competitorDistance / competitorSpeed) < myTimeToReach;
        });
    }

    private calculateTargetAngle(targetPos: Vec3): number {
        const myPos = this.snake.head.getPosition();
        return math.toDegree(Math.atan2(
            targetPos.y - myPos.y,
            targetPos.x - myPos.x
        ));
    }


} 