import { Command } from 'commander';
import { input, select, password } from '@inquirer/prompts';
import { readServiceKey } from '@abapify/adt-auth';
import {
  setDefaultSid,
  getDefaultSid,
  listAvailableSids,
  getAuthManager,
} from '../../utils/auth';
import { handleCommandError } from '../../utils/command-helpers';
import {
  CALLBACK_SERVER_PORT,
  OAUTH_REDIRECT_URI,
  OAUTH_TIMEOUT_MS,
} from '../../config';
import {
  listDestinations,
  getDestination,
  getConfig,
  type Destination,
} from '../../utils/destinations';

interface DestinationOptions {
  url: string;
  [key: string]: unknown;
}

interface DestinationChoice {
  name: string;
  value: { sid: string; destination: Destination | null };
  description: string;
}

export const loginCommand = new Command('login')
  .description('Login to ADT - supports Basic Auth and Browser-based SSO')
  .option(
    '--service-key <path>',
    'Path to a BTP service key JSON file for service-key login',
  )
  .option(
    '--redirect-uri <uri>',
    'OAuth callback redirect URI (e.g. Codespaces/tunnel URL)',
  )
  .option(
    '--insecure',
    'Allow insecure SSL connections (ignore certificate errors)',
  )
  .action(async function (this: Command, options) {
    try {
      // Get SID from global options (--sid is a global option on root program)
      const globalOpts = this.optsWithGlobals();
      const sidArg = globalOpts.sid?.toUpperCase() || '';
      const config = await getConfig();
      const globalRedirectUri =
        typeof config.raw.redirectUri === 'string' &&
        config.raw.redirectUri.trim().length > 0
          ? config.raw.redirectUri
          : undefined;

      console.log('🔐 ADT Login\n');

      if (options.serviceKey) {
        const serviceKey = readServiceKey(options.serviceKey as string);
        if (!serviceKey.systemid && !sidArg) {
          throw new Error(
            'Service key does not contain a systemid. Use --sid to specify the system ID.',
          );
        }

        const sid = sidArg || serviceKey.systemid.toUpperCase();
        const authManager = getAuthManager();
        const openModule = await import('open');
        const openFn = openModule.default;

        const session = await authManager.login(sid, {
          type: '@abapify/adt-auth/plugins/service-key',
          options: {
            url: serviceKey.url,
            serviceKey,
            openBrowser: async (url: string) => {
              try {
                console.log('🌐 Opening browser for authentication...');
                await openFn(url);
              } catch {
                console.log(
                  '🌐 Open this URL in your browser to authenticate:',
                );
                console.log(url);
              }
            },
            callbackPort: CALLBACK_SERVER_PORT,
            redirectUri:
              (options.redirectUri as string | undefined) ??
              globalRedirectUri ??
              OAUTH_REDIRECT_URI,
            timeoutMs: OAUTH_TIMEOUT_MS,
          },
        });

        setDefaultSid(sid);

        console.log(`\n✅ Successfully logged in!`);
        console.log(`   System: ${session.sid}`);
        console.log(`   Host: ${session.host}`);
        console.log(`   Method: ${session.auth.method}`);
        console.log(`   💡 ${sid} set as default system`);

        return;
      }

      // Check if we have configured destinations
      const configuredDestinations = await listDestinations();

      let sid: string = sidArg;

      // If --sid provided and destination exists in config, use it directly
      // Case-insensitive match
      const matchedSid = configuredDestinations.find(
        (d) => d.toUpperCase() === sid,
      );
      if (sid && matchedSid) {
        const dest = await getDestination(matchedSid);
        if (dest) {
          console.log(`📋 Authenticating to ${sid}...\n`);

          // For basic auth without credentials, prompt for username/password
          const destOptions = dest.options as DestinationOptions;
          if (
            dest.type === '@abapify/adt-auth/plugins/basic' &&
            !destOptions.username
          ) {
            const credentials = await promptForBasicAuthCredentials(
              destOptions.url,
              options,
            );
            Object.assign(destOptions, credentials);
          }

          const authManager = getAuthManager();
          const resolvedDestOptions = withGlobalRedirectUri(
            destOptions,
            globalRedirectUri,
          );
          const session = await authManager.login(sid, {
            type: dest.type,
            options: resolvedDestOptions,
          });

          // Set as default
          setDefaultSid(sid);

          console.log(`\n✅ Successfully logged in!`);
          console.log(`   System: ${session.sid}`);
          console.log(`   Host: ${session.host}`);
          console.log(`   Method: ${session.auth.method}`);
          console.log(`   💡 ${sid} set as default system`);

          return;
        }
      }

      // If --sid was explicitly provided but not found, do not fall back to interactive mode.
      if (sid && !matchedSid) {
        throw new Error(
          configuredDestinations.length > 0
            ? `Destination ${sid} not found. Available destinations: ${configuredDestinations.join(', ')}`
            : `Destination ${sid} not found. No destinations are configured.`,
        );
      }

      // If destinations configured but no --sid, show selection
      if (configuredDestinations.length > 0) {
        const destinationChoices: DestinationChoice[] = [];

        for (const destName of configuredDestinations) {
          const dest = await getDestination(destName);
          if (dest) {
            const opts = dest.options as DestinationOptions;
            destinationChoices.push({
              name: `${destName} (${opts.url}) [${dest.type}]`,
              value: { sid: destName, destination: dest },
              description: `${dest.type} authentication`,
            });
          }
        }

        // Add manual option
        destinationChoices.push({
          name: '➕ Manual configuration...',
          value: { sid: '', destination: null as unknown as Destination },
          description: 'Enter URL and auth method manually',
        });

        const selected = await select({
          message: 'Select destination',
          choices: destinationChoices,
        });

        if (selected.destination) {
          sid = selected.sid;
          console.log(`\n📋 Authenticating to ${sid}...\n`);

          const authManager = getAuthManager();
          const destOptions = selected.destination
            .options as DestinationOptions;
          const resolvedDestOptions = withGlobalRedirectUri(
            destOptions,
            globalRedirectUri,
          );
          const session = await authManager.login(sid, {
            type: selected.destination.type,
            options: resolvedDestOptions,
          });

          // Set as default
          setDefaultSid(sid);

          console.log(`\n✅ Successfully logged in!`);
          console.log(`   System: ${session.sid}`);
          console.log(`   Host: ${session.host}`);
          console.log(`   Method: ${session.auth.method}`);
          console.log(`   💡 ${sid} set as default system`);

          return;
        }
      }

      // Manual flow: collect URL and credentials (built-in basic auth only)
      const manualConfig = await promptManualConfig();

      // Get or prompt for SID
      if (!sid) {
        sid = options.sid?.toUpperCase() || (await promptForSid());
      }

      // Collect credentials for basic auth
      const pluginOptions = await collectPluginOptions(
        manualConfig.url,
        options,
      );

      // Authenticate via AuthManager (always uses built-in basic auth for manual flow)
      console.log(`\n📋 Authenticating to ${sid}...\n`);
      const authManager = getAuthManager();
      const session = await authManager.login(sid, {
        type: manualConfig.pluginType,
        options: pluginOptions,
      });

      console.log(`\n✅ Successfully logged in!`);
      console.log(`   System: ${session.sid}`);
      console.log(`   Host: ${session.host}`);
      console.log(`   Method: ${session.auth.method}`);

      // Set as default if it's the first system
      const availableSids = listAvailableSids();
      if (availableSids.length === 1 || !getDefaultSid()) {
        setDefaultSid(sid);
        console.log(`💡 ${sid} set as default system`);
      } else {
        console.log(
          `\n💡 Run "npx adt auth set-default ${sid}" to make it the default system`,
        );
      }
    } catch (error) {
      // Handle user cancellation (Ctrl+C)
      if ((error as any).name === 'ExitPromptError') {
        console.log('\n❌ Login cancelled');
        process.exit(1);
      }
      handleCommandError(error, 'Login');
    }
  });

async function promptForSid(): Promise<string> {
  const sid = await input({
    message: 'System ID (e.g., TRL)',
    validate: (value) => {
      if (!value) return 'SID is required';
      if (!/^[A-Z0-9]{3}$/.test(value.toUpperCase())) {
        return 'SID must be 3 alphanumeric characters (e.g., TRL)';
      }
      return true;
    },
  });
  return sid.toUpperCase();
}

/**
 * Collect authentication options for manual flow
 * (Only built-in basic auth supported in manual mode - other plugins require adt.config.ts)
 */
async function collectPluginOptions(
  url: string,
  commandOptions: any,
): Promise<DestinationOptions> {
  const client = await input({
    message: 'Client (optional, e.g., 100)',
    default: '',
  });

  const username = await input({
    message: 'Username',
    validate: (value) => (value ? true : 'Username is required'),
  });

  const userPassword = await password({
    message: 'Password',
    validate: (value) => (value ? true : 'Password is required'),
  });

  if (commandOptions.insecure) {
    console.log('⚠️  SSL certificate verification disabled\n');
  }

  return {
    url,
    client: client || undefined,
    username,
    password: userPassword,
  };
}

/**
 * Prompt for basic auth credentials only (used when destination in config lacks credentials)
 */
async function promptForBasicAuthCredentials(
  url: string,
  commandOptions: any,
): Promise<{ username: string; password: string; client?: string }> {
  const client = await input({
    message: 'Client (optional, e.g., 100)',
    default: '',
  });

  const username = await input({
    message: 'Username',
    validate: (value) => (value ? true : 'Username is required'),
  });

  const userPassword = await password({
    message: 'Password',
    validate: (value) => (value ? true : 'Password is required'),
  });

  if (commandOptions.insecure) {
    console.log('⚠️  SSL certificate verification disabled\n');
  }

  return {
    client: client || undefined,
    username,
    password: userPassword,
  };
}

/**
 * Prompt for manual configuration (no adt.config.ts)
 */
async function promptManualConfig(): Promise<{
  pluginType: string;
  url: string;
}> {
  // Step 1: Choose authentication method (built-in plugins only)
  const pluginType = await select({
    message: 'Authentication method',
    choices: [
      {
        name: 'Basic Authentication (username/password)',
        value: '@abapify/adt-auth/plugins/basic',
        description: 'Standard username and password authentication',
      },
    ],
  });

  console.log('');

  // Step 2: Collect URL
  let url = await input({
    message: 'SAP System URL (e.g., https://host:port or host:port)',
    validate: (value) => {
      if (!value) return 'URL is required';
      try {
        new URL(value.includes('://') ? value : `https://${value}`);
        return true;
      } catch {
        return 'Please enter a valid URL or hostname';
      }
    },
  });

  // Normalize URL
  if (!url.includes('://')) {
    url = `https://${url}`;
  }

  return { pluginType, url };
}

function withGlobalRedirectUri(
  options: DestinationOptions,
  globalRedirectUri?: string,
): DestinationOptions {
  if (!globalRedirectUri || options.redirectUri) {
    return options;
  }

  return {
    ...options,
    redirectUri: globalRedirectUri,
  };
}
