
import { useEffect, useState } from 'react';
import {
    GITEA_URL,
    GITEA_OWNER,
    GITEA_REPO,
    RELEASE_TAG_PREFIX,
    UPDATE_CHECK_TIMEOUT_MS,
} from '../config/gitea';
import { version as currentVersion } from '../version.json';

export interface UpdateInfo {
    /** Build number of the latest release (same unit as currentVersion). */
    latestVersion: number;
    /** Direct URL the user can open to read the release notes / download the APK. */
    releaseUrl: string;
    /** Tag name as stored on Gitea, e.g. "mobile-v42". */
    tagName: string;
}

export interface UseUpdateCheckResult {
    /** True while the API call is in flight. */
    checking: boolean;
    /** Populated once the check completes and a newer build exists. */
    updateInfo: UpdateInfo | null;
    /** Non-null when the check failed (network error, timeout, bad response). */
    error: Error | null;
    /** Re-run the check manually (e.g. from a "check again" button). */
    recheck: () => void;
}

// ─── Gitea releases API ──────────────────────────────────────────────────────

interface GiteaRelease {
    id: number;
    tag_name: string;
    name: string;
    html_url: string;
    draft: boolean;
    prerelease: boolean;
}

async function fetchLatestRelease(): Promise<GiteaRelease> {
    const url = `${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/latest`;

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);

    try {
        const res = await fetch(url, { signal: controller.signal });

        if (!res.ok) {
            throw new Error(`Gitea API returned HTTP ${res.status}`);
        }

        return (await res.json()) as GiteaRelease;
    } finally {
        clearTimeout(timer);
    }
}

/** Parse the integer build number out of a tag like "mobile-v42". Returns NaN if it can't. */
function parseBuildNumber(tagName: string): number {
    if (!tagName.startsWith(RELEASE_TAG_PREFIX)) return NaN;
    return parseInt(tagName.slice(RELEASE_TAG_PREFIX.length), 10);
}


export function useUpdatecheck(): UseUpdateCheckResult {
    const [checking, setChecking] = useState(false);
    const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
    const [error, setError] = useState<Error | null>(null);
    // A counter we bump to re-trigger the effect without changing the dep array shape.
    const [trigger, setTrigger] = useState(0);

    useEffect(() => {
        let cancelled = false;

        async function check() {
            setChecking(true);
            setError(null);

            try {
                const release = await fetchLatestRelease();

                if (cancelled) return;

                // Skip drafts and pre-releases.
                if (release.draft || release.prerelease) {
                    setUpdateInfo(null);
                    return;
                }

                const latestVersion = parseBuildNumber(release.tag_name);

                if (isNaN(latestVersion)) {
                    // The tag doesn't follow our scheme — ignore it silently.
                    setUpdateInfo(null);
                    return;
                }

                if (latestVersion > currentVersion) {
                    setUpdateInfo({
                        latestVersion,
                        releaseUrl: release.html_url,
                        tagName: release.tag_name,
                    });
                } else {
                    setUpdateInfo(null);
                }
            } catch (err) {
                if (cancelled) return;
                // Don't surface AbortError as a real error — it's just a timeout.
                if (err instanceof Error && err.name === 'AbortError') {
                    setError(new Error('Update check timed out. Check your connection.'));
                } else {
                    setError(err instanceof Error ? err : new Error(String(err)));
                }
            } finally {
                if (!cancelled) setChecking(false);
            }
        }

        check();

        return () => {
            cancelled = true;
        };
    }, [trigger]);

    return {
        checking,
        updateInfo,
        error,
        recheck: () => setTrigger(t => t + 1),
    };
}