#!/usr/bin/env node
import { fileURLToPath } from 'url';
import path from 'path';
import { spawn } from 'child_process';
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';
import chalk from 'chalk';

// Re-run postinstall only when an update is actually needed
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const postinstall = path.join(__dirname, 'postinstall.js');

const BIN_NAME = 'maravilla';
const REPO = process.env.MARAVILLA_CLI_REPO || 'maravilla-labs/maravilla-cli';
const BIN_DIR = path.join(__dirname, '..', 'bin');
const EXECUTABLE = process.platform === 'win32' ? `${BIN_NAME}.exe` : BIN_NAME;
const INSTALL_PATH = path.join(BIN_DIR, EXECUTABLE);
const GH_TOKEN = process.env.MARAVILLA_GH_TOKEN || process.env.GITHUB_TOKEN || '';
const PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || '';
const HTTP_TIMEOUT_MS = Number(process.env.MARAVILLA_HTTP_TIMEOUT_MS || 15000);
const HTTP_RETRIES = Math.max(1, Number(process.env.MARAVILLA_HTTP_RETRIES || 3));

function flower() {
	return chalk.magenta('✿');
}

function log(msg) {
	console.log(`${flower()} ${msg}`);
}

async function currentVersion() {
	return new Promise((resolve) => {
		const child = spawn(INSTALL_PATH, ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] });
		let out = '';
		child.stdout.on('data', (d) => (out += d.toString()));
		child.on('error', () => resolve(null));
		child.on('exit', (code) => {
			if (code === 0) {
				const v = (out || '').trim();
				// clap default prints: "maravilla X.Y.Z" — extract semver
				const match = v.match(/(\d+\.\d+\.\d+)/);
				resolve(match ? match[1] : null);
			} else {
				resolve(null);
			}
		});
	});
}

function makeFetchOpts() {
	const headers = { 'User-Agent': 'maravilla-cli-updater' };
	if (GH_TOKEN) headers['Authorization'] = `Bearer ${GH_TOKEN}`;
	const opts = { headers };
	if (PROXY) {
		try { opts.agent = new HttpsProxyAgent(PROXY); } catch {}
	}
	return opts;
}

async function latestVersion() {
	const api = `https://api.github.com/repos/${REPO}/releases/latest`;
	const opts = makeFetchOpts();
	try {
		const controller = new AbortController();
		const t = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
		const res = await fetch(api, { ...opts, signal: controller.signal });
		clearTimeout(t);
		if (!res.ok) throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);
		const json = await res.json();
		const tag = json.tag_name || '';
		const m = tag.match(/v(\d+\.\d+\.\d+)/);
		return { tag, version: m ? m[1] : null };
	} catch (e) {
		// Fallback to HTML redirect
		const html = `https://github.com/${REPO}/releases/latest`;
		const res = await fetch(html, { ...opts, redirect: 'manual' });
		const loc = res.headers.get('location') || '';
		const m = loc.match(/\/releases\/tag\/([^/]+)$/);
		const tag = m ? m[1] : '';
		const mv = tag.match(/v(\d+\.\d+\.\d+)/);
		return { tag, version: mv ? mv[1] : null };
	}
}

function cmpSemver(a, b) {
	if (!a || !b) return 1; // treat unknown as needs update
	const pa = a.split('.').map(Number);
	const pb = b.split('.').map(Number);
	for (let i = 0; i < 3; i++) {
		if (pa[i] > pb[i]) return 1;
		if (pa[i] < pb[i]) return -1;
	}
	return 0;
}

async function main() {
	// Allow forcing a version via env (MARAVILLA_VERSION)
	const forced = process.env.MARAVILLA_VERSION?.replace(/^v/, '') || null;
	const cur = await currentVersion();
	const { tag, version: latest } = await latestVersion();

	if (forced) {
		log(chalk.cyan(`Forcing update to v${forced}`));
	} else if (cur && latest && cmpSemver(cur, latest) >= 0) {
		log(chalk.green(`Already up to date (v${cur})`));
		return 0;
	} else if (cur && latest) {
		log(`Updating from v${cur} to ${chalk.cyan(tag)}`);
	} else {
		log(`Updating to ${chalk.cyan(tag)}`);
	}

	// Force the specific version we want
	const env = { 
		...process.env, 
		MARAVILLA_FORCE_INSTALL: '1',
		MARAVILLA_VERSION: tag  // Pass the tag to ensure we get the right version
	};
	
	const result = await new Promise((resolve) => {
		const child = spawn(process.execPath, [postinstall], { stdio: 'inherit', env });
		child.on('exit', (code) => resolve(code ?? 0));
	});
	
	// Verify the update worked
	if (result === 0) {
		const newVersion = await currentVersion();
		if (newVersion) {
			log(chalk.green(`✅ Successfully updated to v${newVersion}`));
		}
	}
	
	return result;
}

main().then((code) => process.exit(code)).catch((e) => {
	console.error(chalk.red(`${flower()} update failed: ${e.message}`));
	process.exit(1);
});
