'use strict';

const Vec3 = require('vec3');
const { goals: { GoalNear } } = require('mineflayer-pathfinder');
const { sleep } = require('../../utils');

class Navigation {
	constructor(args) {
		this.bot = args.bot;

		this.commands = [
			{
				name: 'goto',
				description: 'Move the bot to coordinates or a player position',
				parameters: [
					{ name: 'target', type: 'string', required: true, description: 'Coordinates "x y z" or player name' },
					{ name: 'range', type: 'number', required: false, description: 'Stop distance from target (default: 0)' }
				],
				category: 'movement'
			},
			{
				name: 'come',
				description: 'Come to the requesting player with recovery on failure',
				parameters: [
					{ name: 'player', type: 'string', required: false, description: 'Player to come to (defaults to requester)' }
				],
				category: 'movement'
			},
			{
				name: 'follow',
				description: 'Go to a player and maintain distance',
				parameters: [
					{ name: 'target', type: 'string', required: true, description: 'Player to follow' },
					{ name: 'range', type: 'number', required: false, description: 'Follow distance (default: 3)' }
				],
				category: 'movement'
			},
			{
				name: 'stop',
				description: 'Stop current pathfinding',
				parameters: [],
				category: 'movement'
			}
		];
	}

	async init() {}
	async unload() {
		try { this.bot.bot.pathfinder.stop(); } catch (e) { /* ignore */ }
		return true;
	}

	async handleCommand(from, command, ...args) {
		switch (command) {
			case 'goto': {
				const [target, rangeStr] = args;
				const range = parseFloat(rangeStr) || 0;
				if (!target) return 'Specify coordinates "x y z" or a player name';

				// Parse coordinates
				const parts = target.split(' ');
				if (parts.length >= 3) {
					const coords = parts.map(Number);
					if (coords.every(c => !isNaN(c))) {
						return this._goWithRecovery(new Vec3(coords[0], coords[1], coords[2]), range);
					}
				}

				// Player target
				const player = this.bot.bot.players[target];
				if (player && player.entity) {
					return this._goWithRecovery(player.entity.position, range);
				}
				return `Target not found: ${target}`;
			}

			case 'come': {
				const [playerName] = args;
				const target = playerName || from;
				const player = this.bot.bot.players[target];
				if (!player || !player.entity) return `Cannot find ${target}`;
				return this._goWithRecovery(player.entity.position, 3);
			}

			case 'follow': {
				const [target, rangeStr] = args;
				const range = parseFloat(rangeStr) || 3;
				const player = this.bot.bot.players[target];
				if (!player || !player.entity) return `Cannot find ${target}`;
				return this._goWithRecovery(player.entity.position, range);
			}

			case 'stop':
				try { this.bot.bot.pathfinder.stop(); } catch (e) { /* ignore */ }
				return 'Movement stopped';

			default:
				return `Unknown navigation command: ${command}`;
		}
	}

	// Smart movement with recovery — retries with 360 scan and waypoint unstick when goTo fails
	async _goWithRecovery(targetPos, range) {
		let attempts = 0;

		while (attempts < 3) {
			const ok = await this.bot.goTo({ where: targetPos, range });
			if (ok) return `Arrived at destination`;

			attempts++;
			console.log(`[Navigation] goTo failed (attempt ${attempts}/3), applying recovery...`);

			// 360 visual refresh
			const bot = this.bot.bot;
			for (let i = 0; i < 4; i++) {
				await bot.look(bot.entity.yaw + Math.PI / 2, 0, true);
				await sleep(150);
			}

			// Perpendicular waypoint to unstick
			const pos = bot.entity.position;
			if (!isNaN(pos.x) && !isNaN(pos.z)) {
				const yaw = Math.atan2(targetPos.z - pos.z, targetPos.x - pos.x);
				const perps = [yaw + Math.PI / 2, yaw - Math.PI / 2, yaw + Math.PI];
				for (const p of perps) {
					const wx = pos.x + Math.cos(p) * 3;
					const wz = pos.z + Math.sin(p) * 3;
					try {
						await bot.pathfinder.goto(
							new GoalNear(wx, pos.y, wz, 1)
						);
						await sleep(1000);
						bot.clearControlStates();
						break;
					} catch (e) { /* try next */ }
				}

				// Backward nudge
				bot.setControlState('back', true);
				await sleep(300);
				bot.clearControlStates();
				await sleep(200);
			}
		}

		return 'Failed to reach destination after recovery attempts';
	}
}

module.exports = Navigation;
