import { Command } from "jsr:@cliffy/command@1.0.0-rc.7";
import { Secret } from "jsr:@cliffy/prompt@1.0.0-rc.7";
import { homedir } from "node:os";
import { TokenApi } from "./api/token-api.ts";
import { OAuth2Api } from "./api/oauth2-api.ts";
import { CredentialManager } from "./util/credential-manager.ts";
import { prettyOauth2Application, saveSecretKeys } from "./util/helpers.ts";

// Constants that were previously in types.ts
const URL_BASE = "https://git.monoid.top";
const TOKEN_NAME = "oauth2cli";
const CREDENTIALS_FILE = `${homedir()}/.oauth2cli-forgejo`;

async function main() {
    const tokenApi = new TokenApi(URL_BASE, TOKEN_NAME);
    const credentialManager = new CredentialManager(CREDENTIALS_FILE, TOKEN_NAME);

    try {
        await new Command()
            .name("forgejo-oauth2cli")
            .version("0.1.0")
            .description("Interactive client credentials generator for Forgejo.")
            .command("login", "Authenticate with Forgejo interactively")
            .action(async () => {
                const username = await prompt("Enter your username: ");
                if (!username) {
                    console.error("Username is required.");
                    return;
                }

                const secret = await Secret.prompt("Enter your secret: ");
                if (!secret) {
                    console.error("Secret is required.");
                    return;
                }

                // Check for existing tokens
                const tokens = await tokenApi.listTokens(username, secret);
                if (!tokens) {
                    return;
                }

                // Delete existing token if found
                const existingToken = tokens.find(t => t.name === TOKEN_NAME);
                if (existingToken) {
                    console.log("Existing token found, replacing it...");
                    await tokenApi.deleteToken(username, secret, existingToken.id);
                }

                // Create new token
                console.log("Creating new token...");
                const token = await tokenApi.createToken(username, secret);
                if (!token) {
                    return;
                }

                // Store credentials
                await credentialManager.storeCredentials(username, token.sha1);
                console.log("Login successful! Credentials stored securely.");
            })
            .command("logout", "Delete token and credentials")
            .action(async () => {
                const username = await prompt("Enter your username: ");
                if (!username) {
                    console.error("Username is required.");
                    return;
                }

                const secret = await Secret.prompt("Enter your secret: ");
                if (!secret) {
                    console.error("Secret is required.");
                    return;
                }

                // Find and delete token
                const tokens = await tokenApi.listTokens(username, secret);
                if (!tokens) {
                    return;
                }

                const existingToken = tokens.find(t => t.name === TOKEN_NAME);
                if (existingToken) {
                    await tokenApi.deleteToken(username, secret, existingToken.id);
                    console.log("Token deleted from Forgejo.");
                }

                // Clear stored credentials
                await credentialManager.clearCredentials(username);
                console.log("Logout successful! Credentials removed.");
            })
            .command("list, ls", "List all OAuth2 applications")
            .option("--pretty, -p", "Pretty print the output")
            .action(async (opt) => {
                const credentials = await credentialManager.getCredentials();
                if (!credentials) return;

                // Create OAuth2Api with token
                const oauth2Api = new OAuth2Api(URL_BASE, credentials.token);
                const apps = await oauth2Api.getOauth2Applications();
                if (!apps) return;

                if (apps.length === 0) {
                    console.log("No OAuth2 applications found.");
                    return;
                }
                if (opt.pretty) {
                    console.log("OAuth2 applications:");
                    console.log(apps.map(prettyOauth2Application).join("\n\n"));
                    return;
                }
                else {
                    const maxNameLength = Math.max(...apps.map(app => app.name.length), 6);
                    console.log(apps.map(app => [
                        app.id.toString().padEnd(5),
                        app.confidential_client ? "c" : "o",
                        app.name.padStart(maxNameLength),
                        app.client_id,
                        app.redirect_uris.join(", "),
                    ].join(" ").trim()).join("\n"));
                }
            })
            .command("create", "Create new OAuth2 application")
            .action(async () => {
                const credentials = await credentialManager.getCredentials();
                if (!credentials) {
                    console.error("Credentials not found. Please login first.");
                    return;
                }

                // Create OAuth2Api with token
                const oauth2Api = new OAuth2Api(URL_BASE, credentials.token);

                const name = await prompt("Enter the name of the application: ");
                if (!name) {
                    console.error("Name is required.");
                    return;
                }

                const redirectUris = await prompt("Enter the redirect URIs (comma separated): ");
                if (!redirectUris) {
                    console.error("Redirect URIs are required.");
                    return;
                }

                const app = await oauth2Api.createOauth2Application({
                    name,
                    redirect_uris: redirectUris.split(",").map(uri => uri.trim()),
                });

                if (!app) return;

                console.log("OAuth2 application created successfully!");
                console.log(prettyOauth2Application(app));

                // Save secret keys
                const path = await prompt("Enter the path to save the OAuth2 secret keys: ");
                if (path) {
                    await saveSecretKeys(path, app.client_id, app.client_secret);
                }
            })
            .command("remove, rm", "Remove OAuth2 application")
            .arguments("<id:number>")
            .action(async (_opt, appId) => {
                const credentials = await credentialManager.getCredentials();
                if (!credentials) {
                    console.error("Credentials not found. Please login first.");
                    return;
                }

                // Create OAuth2Api with token
                const oauth2Api = new OAuth2Api(URL_BASE, credentials.token);

                await oauth2Api.deleteOauth2Application(appId);
                console.log("OAuth2 application deleted successfully!");
            })
            .command("update", "Update OAuth2 application")
            .action(async () => {
                const credentials = await credentialManager.getCredentials();
                if (!credentials) {
                    console.error("Credentials not found. Please login first.");
                    return;
                }

                // Create OAuth2Api with token
                const oauth2Api = new OAuth2Api(URL_BASE, credentials.token);

                const id = await prompt("Enter the ID of the application to update: ");
                if (!id) {
                    console.error("ID is required.");
                    return;
                }

                const appId = parseInt(id);
                if (isNaN(appId)) {
                    console.error("ID must be a number.");
                    return;
                }

                const name = await prompt("Enter the new name of the application: ");
                if (!name) {
                    console.error("Name is required.");
                    return;
                }

                const redirectUris = await prompt("Enter the new redirect URIs (comma separated): ");
                if (!redirectUris) {
                    console.error("Redirect URIs are required.");
                    return;
                }

                const app = await oauth2Api.updateOauth2Application(appId, {
                    name,
                    redirect_uris: redirectUris.split(",").map(uri => uri.trim()),
                });

                if (!app) return;

                console.log("OAuth2 application updated successfully!");
                console.log(prettyOauth2Application(app));

                // Save secret keys
                const path = await prompt("Enter the path to save the OAuth2 secret keys: ");
                if (path) {
                    await saveSecretKeys(path, app.client_id, app.client_secret);
                }
            })
            .command("fetch-config", "Fetch OpenID configuration")
            .action(async () => {
                const response = await fetch(`${URL_BASE}/.well-known/openid-configuration`);
                if (!response.ok) {
                    console.error("Failed to fetch OpenID configuration:", response.statusText);
                    return;
                }
                
                const config = await response.json();
                console.log("OpenID Configuration:", JSON.stringify(config, null, 2));
            })
            .parse(Deno.args);
    } catch (error) {
        if (error instanceof Error) {
            console.error("An error occurred:", error.message);
        }
        else {
            console.error("An unexpected error occurred:", error);
        }
    }
}

if (import.meta.main) {
    await main();
}