import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IEmbyConfig, IEmbySnapshot } from './emby.types.js';
import { embyDefaultHttpPort, embyDefaultHttpsPort, embyDefaultTimeoutMs } from './emby.types.js';

export class EmbyConfigFlow implements IConfigFlow<IEmbyConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IEmbyConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect Emby',
      description: 'Configure a local Emby server endpoint. Session polling and media control require an Emby API key, injected client, or command executor.',
      fields: [
        { name: 'url', label: 'Server URL', type: 'text', required: true },
        { name: 'apiKey', label: 'API key', type: 'password' },
        { name: 'name', label: 'Name', type: 'text' },
        { name: 'userId', label: 'Controlling user ID', type: 'text' },
      ],
      submit: async (valuesArg) => ({
        kind: 'done',
        title: 'Emby configured',
        config: this.configFromValues(candidateArg, valuesArg),
      }),
    };
  }

  private configFromValues(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): IEmbyConfig {
    const metadata = candidateArg.metadata || {};
    const snapshot = this.snapshotMetadata(metadata);
    const url = this.stringValue(valuesArg.url) || this.urlFromCandidate(candidateArg);
    const parsedUrl = this.parseUrl(url);
    const ssl = parsedUrl?.ssl ?? (candidateArg.metadata?.ssl === true);

    return {
      url,
      host: parsedUrl?.host || candidateArg.host,
      port: parsedUrl?.port || candidateArg.port || (ssl ? embyDefaultHttpsPort : embyDefaultHttpPort),
      ssl,
      apiKey: this.stringValue(valuesArg.apiKey) || this.stringMetadata(metadata, 'apiKey'),
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.server.ServerName || snapshot?.server.Name,
      uniqueId: candidateArg.id || snapshot?.server.Id || snapshot?.server.ServerId,
      userId: this.stringValue(valuesArg.userId) || this.stringMetadata(metadata, 'userId'),
      clientDeviceId: `smarthome-exchange-${candidateArg.id || candidateArg.host || 'emby'}`,
      timeoutMs: embyDefaultTimeoutMs,
      snapshot,
      server: snapshot?.server || this.serverMetadata(metadata),
      sessions: snapshot?.sessions || this.sessionsMetadata(metadata),
      client: metadata.client as IEmbyConfig['client'],
      commandExecutor: metadata.commandExecutor as IEmbyConfig['commandExecutor'],
    };
  }

  private urlFromCandidate(candidateArg: IDiscoveryCandidate): string {
    const explicitUrl = candidateArg.metadata?.url;
    if (typeof explicitUrl === 'string' && explicitUrl) {
      return explicitUrl;
    }
    const ssl = candidateArg.metadata?.ssl === true;
    const protocol = ssl ? 'https' : 'http';
    const port = candidateArg.port || (ssl ? embyDefaultHttpsPort : embyDefaultHttpPort);
    return candidateArg.host ? `${protocol}://${candidateArg.host}:${port}` : '';
  }

  private parseUrl(valueArg: string | undefined): { host: string; port?: number; ssl: boolean } | undefined {
    if (!valueArg) {
      return undefined;
    }
    try {
      const url = new URL(valueArg);
      return {
        host: url.hostname,
        port: url.port ? Number(url.port) : undefined,
        ssl: url.protocol === 'https:',
      };
    } catch {
      return undefined;
    }
  }

  private snapshotMetadata(metadataArg: Record<string, unknown>): IEmbySnapshot | undefined {
    const snapshot = metadataArg.snapshot;
    if (snapshot && typeof snapshot === 'object' && 'server' in snapshot && 'sessions' in snapshot) {
      return snapshot as IEmbySnapshot;
    }
    return undefined;
  }

  private serverMetadata(metadataArg: Record<string, unknown>): IEmbyConfig['server'] | undefined {
    const server = metadataArg.server;
    return server && typeof server === 'object' ? server as IEmbyConfig['server'] : undefined;
  }

  private sessionsMetadata(metadataArg: Record<string, unknown>): IEmbyConfig['sessions'] | undefined {
    const sessions = metadataArg.sessions;
    return Array.isArray(sessions) ? sessions as IEmbyConfig['sessions'] : undefined;
  }

  private stringMetadata(metadataArg: Record<string, unknown>, keyArg: string): string | undefined {
    const value = metadataArg[keyArg];
    return typeof value === 'string' && value.trim() ? value.trim() : undefined;
  }

  private stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
  }
}
