import https from 'https';
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT_DIR = path.resolve(__dirname, '..');

const UPSTREAM_REPO = 'https://github.com/sickn33/antigravity-awesome-skills.git';
const UPSTREAM_NAME = 'upstream';
const REPO_TAR_URL = 'https://github.com/sickn33/antigravity-awesome-skills/archive/refs/heads/main.tar.gz';
const REPO_ZIP_URL = 'https://github.com/sickn33/antigravity-awesome-skills/archive/refs/heads/main.zip';
const COMMITS_API_URL = 'https://api.github.com/repos/sickn33/antigravity-awesome-skills/commits/main';
const SHA_FILE = path.join(__dirname, '.last-sync-sha');

// ─── Utility helpers ───

const MIME_TYPES = {
    '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript',
    '.json': 'application/json', '.md': 'text/markdown', '.txt': 'text/plain',
    '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
    '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon',
    '.yaml': 'text/yaml', '.yml': 'text/yaml', '.xml': 'text/xml',
    '.py': 'text/plain', '.sh': 'text/plain', '.bat': 'text/plain',
};

/** Check if git is available on this system. Cached after first check. */
let _gitAvailable = null;
function isGitAvailable() {
    if (_gitAvailable !== null) return _gitAvailable;
    try {
        execSync('git --version', { stdio: 'ignore' });
        // Also check we're inside a git repo
        execSync('git rev-parse --git-dir', { cwd: ROOT_DIR, stdio: 'ignore' });
        _gitAvailable = true;
    } catch {
        _gitAvailable = false;
    }
    return _gitAvailable;
}

/** Run a git command in the project root. */
function git(cmd) {
    return execSync(`git ${cmd}`, { cwd: ROOT_DIR, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
}

/** Ensure the upstream remote exists. */
function ensureUpstream() {
    const remotes = git('remote');
    if (!remotes.split('\n').includes(UPSTREAM_NAME)) {
        git(`remote add ${UPSTREAM_NAME} ${UPSTREAM_REPO}`);
        console.log(`[Sync] Added upstream remote: ${UPSTREAM_REPO}`);
    }
}

/** Download a file following HTTP redirects. */
function downloadFile(url, dest) {
    return new Promise((resolve, reject) => {
        const file = fs.createWriteStream(dest);
        const request = (url) => {
            https.get(url, { headers: { 'User-Agent': 'antigravity-skills-app' } }, (res) => {
                if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
                    request(res.headers.location);
                    return;
                }
                if (res.statusCode !== 200) {
                    reject(new Error(`Download failed with status ${res.statusCode}`));
                    return;
                }
                res.pipe(file);
                file.on('finish', () => { file.close(); resolve(); });
            }).on('error', (err) => { fs.unlink(dest, () => { }); reject(err); });
        };
        request(url);
    });
}

/** Check latest commit SHA via GitHub API. */
function checkRemoteSha() {
    return new Promise((resolve) => {
        https.get(COMMITS_API_URL, {
            headers: { 'User-Agent': 'antigravity-skills-app', 'Accept': 'application/vnd.github.v3+json' },
        }, (res) => {
            let body = '';
            res.on('data', (chunk) => { body += chunk; });
            res.on('end', () => {
                try {
                    if (res.statusCode === 200) {
                        resolve(JSON.parse(body).sha || null);
                    } else {
                        resolve(null);
                    }
                } catch {
                    resolve(null);
                }
            });
        }).on('error', () => resolve(null));
    });
}

/** Copy folder recursively. */
function copyFolderSync(from, to) {
    if (!fs.existsSync(to)) fs.mkdirSync(to, { recursive: true });
    for (const element of fs.readdirSync(from)) {
        const srcPath = path.join(from, element);
        const destPath = path.join(to, element);
        if (fs.lstatSync(srcPath).isFile()) {
            fs.copyFileSync(srcPath, destPath);
        } else {
            copyFolderSync(srcPath, destPath);
        }
    }
}

// ─── Sync strategies ───

/**
 * FAST PATH: Use git fetch + merge (only downloads delta).
 * Typically completes in 5-15 seconds.
 */
async function syncWithGit() {
    ensureUpstream();

    const headBefore = git('rev-parse HEAD');

    console.log('[Sync] Fetching from upstream (git)...');
    git(`fetch ${UPSTREAM_NAME} main`);

    const upstreamHead = git(`rev-parse ${UPSTREAM_NAME}/main`);

    if (headBefore === upstreamHead) {
        return { upToDate: true };
    }

    console.log('[Sync] Merging updates...');
    try {
        git(`merge ${UPSTREAM_NAME}/main --ff-only`);
    } catch {
        console.log('[Sync] Fast-forward failed, resetting to upstream...');
        git(`reset --hard ${UPSTREAM_NAME}/main`);
    }

    return { upToDate: false };
}

/**
 * FALLBACK: Download archive when git is not available.
 * Tries tar.gz first (faster), falls back to zip if tar isn't available.
 */
async function syncWithArchive() {
    // Check SHA first to skip if up to date
    const remoteSha = await checkRemoteSha();
    if (remoteSha) {
        let storedSha = null;
        if (fs.existsSync(SHA_FILE)) {
            storedSha = fs.readFileSync(SHA_FILE, 'utf-8').trim();
        }
        if (storedSha === remoteSha) {
            return { upToDate: true };
        }
    }

    const tempDir = path.join(ROOT_DIR, 'update_temp');

    // Try tar first, fall back to zip
    let useTar = true;
    try {
        execSync('tar --version', { stdio: 'ignore' });
    } catch {
        useTar = false;
    }

    const archivePath = path.join(ROOT_DIR, useTar ? 'update.tar.gz' : 'update.zip');

    try {
        // 1. Download
        console.log(`[Sync] Downloading (${useTar ? 'tar.gz' : 'zip'})...`);
        await downloadFile(useTar ? REPO_TAR_URL : REPO_ZIP_URL, archivePath);

        // 2. Extract
        console.log('[Sync] Extracting...');
        if (fs.existsSync(tempDir)) fs.rmSync(tempDir, { recursive: true, force: true });
        fs.mkdirSync(tempDir, { recursive: true });

        if (useTar) {
            execSync(`tar -xzf "${archivePath}" -C "${tempDir}"`, { stdio: 'ignore' });
        } else if (process.platform === 'win32') {
            execSync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tempDir}' -Force"`, { stdio: 'ignore' });
        } else {
            execSync(`unzip -o "${archivePath}" -d "${tempDir}"`, { stdio: 'ignore' });
        }

        // 3. Move skills to root
        const extractedRoot = path.join(tempDir, 'antigravity-awesome-skills-main');
        const srcSkills = path.join(extractedRoot, 'skills');
        const srcIndex = path.join(extractedRoot, 'skills_index.json');
        const destSkills = path.join(ROOT_DIR, 'skills');
        const destIndex = path.join(ROOT_DIR, 'skills_index.json');

        if (!fs.existsSync(srcSkills)) {
            throw new Error('Skills folder not found in downloaded archive.');
        }

        console.log('[Sync] Updating skills...');
        if (fs.existsSync(destSkills)) fs.rmSync(destSkills, { recursive: true, force: true });
        fs.renameSync(srcSkills, destSkills);
        if (fs.existsSync(srcIndex)) fs.copyFileSync(srcIndex, destIndex);

        // Save SHA
        if (remoteSha) fs.writeFileSync(SHA_FILE, remoteSha, 'utf-8');

        return { upToDate: false };

    } finally {
        if (fs.existsSync(archivePath)) fs.unlinkSync(archivePath);
        if (fs.existsSync(tempDir)) fs.rmSync(tempDir, { recursive: true, force: true });
    }
}

// ─── Vite Plugin ───

export default function refreshSkillsPlugin() {
    return {
        name: 'refresh-skills',
        configureServer(server) {
            // Serve /skills.json directly from ROOT_DIR
            server.middlewares.use('/skills.json', (req, res, next) => {
                const filePath = path.join(ROOT_DIR, 'skills_index.json');
                if (fs.existsSync(filePath)) {
                    res.setHeader('Content-Type', 'application/json');
                    fs.createReadStream(filePath).pipe(res);
                } else {
                    next();
                }
            });

            // Serve /skills/* directly from ROOT_DIR/skills/
            server.middlewares.use((req, res, next) => {
                if (!req.url || !req.url.startsWith('/skills/')) return next();

                const relativePath = decodeURIComponent(req.url.replace(/\?.*$/, ''));
                const filePath = path.join(ROOT_DIR, relativePath);

                const resolved = path.resolve(filePath);
                if (!resolved.startsWith(path.join(ROOT_DIR, 'skills'))) return next();

                if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
                    const ext = path.extname(filePath).toLowerCase();
                    res.setHeader('Content-Type', MIME_TYPES[ext] || 'application/octet-stream');
                    fs.createReadStream(filePath).pipe(res);
                } else {
                    next();
                }
            });

            // Sync API endpoint
            server.middlewares.use('/api/refresh-skills', async (req, res) => {
                res.setHeader('Content-Type', 'application/json');

                try {
                    let result;

                    if (isGitAvailable()) {
                        console.log('[Sync] Using git (fast path)...');
                        result = await syncWithGit();
                    } else {
                        console.log('[Sync] Git not available, using archive download (slower)...');
                        result = await syncWithArchive();
                    }

                    if (result.upToDate) {
                        console.log('[Sync] ✅ Already up to date!');
                        res.end(JSON.stringify({ success: true, upToDate: true }));
                        return;
                    }

                    // Count skills
                    const indexPath = path.join(ROOT_DIR, 'skills_index.json');
                    let count = 0;
                    if (fs.existsSync(indexPath)) {
                        const data = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
                        count = Array.isArray(data) ? data.length : 0;
                    }

                    console.log(`[Sync] ✅ Successfully synced ${count} skills!`);
                    res.end(JSON.stringify({ success: true, upToDate: false, count }));

                } catch (err) {
                    console.error('[Sync] ❌ Failed:', err.message);
                    res.statusCode = 500;
                    res.end(JSON.stringify({ success: false, error: err.message }));
                }
            });
        }
    };
}
