import { invoke } from '@tauri-apps/api/core';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import {
  AgentStreamItemEvent,
  BackgroundJobSnapshot,
  CancelBackgroundJobRequest,
  CancelChatStreamRequest,
  ChatStreamStart,
  LookupCompanyRequest,
  ExecuteTerminalCommandRequest,
  ResolveAgentToolApprovalRequest,
  StartBackgroundJobRequest,
  StartChatStreamRequest,
  TerminalCommandResponse,
} from '../types/terminal';
import { Company } from '../types/financial';

interface StreamCallbacks {
  workspaceId: string;
  onStreamItem: (event: Omit<AgentStreamItemEvent, 'response'> & {
    response?: TerminalCommandResponse;
  }) => void;
}

export const createTerminalRequestId = (): string => {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }

  return `request-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
};

class TerminalBridge {
  private listenersReady: Promise<void> | null = null;
  private unlistenFns: UnlistenFn[] = [];
  private streamCallbacks = new Map<string, StreamCallbacks>();

  private async ensureListeners() {
    if (this.listenersReady) {
      return this.listenersReady;
    }

    this.listenersReady = Promise.all([
      listen<AgentStreamItemEvent>('agent_stream_item', (event) => {
        const callbacks = this.streamCallbacks.get(event.payload.requestId);
        if (!callbacks || callbacks.workspaceId !== event.payload.workspaceId) {
          return;
        }
        callbacks.onStreamItem({
          ...event.payload,
          response: event.payload.response,
        });
        if (
          event.payload.kind === 'stream_complete' ||
          event.payload.kind === 'cancelled' ||
          event.payload.kind === 'error'
        ) {
          this.streamCallbacks.delete(event.payload.requestId);
        }
      }),
    ]).then((unlistenFns) => {
      this.unlistenFns = unlistenFns;
    });

    return this.listenersReady;
  }

  async executeTerminalCommand(
    request: ExecuteTerminalCommandRequest,
  ): Promise<TerminalCommandResponse> {
    return invoke<TerminalCommandResponse>('execute_terminal_command', {
      request,
    });
  }

  async lookupCompany(request: LookupCompanyRequest): Promise<Company> {
    return invoke<Company>('lookup_company', {
      request,
    });
  }

  async startChatStream(
    request: StartChatStreamRequest,
    callbacks: Omit<StreamCallbacks, 'workspaceId'>,
  ): Promise<ChatStreamStart> {
    await this.ensureListeners();

    this.streamCallbacks.set(request.requestId, {
      workspaceId: request.workspaceId,
      ...callbacks,
    });

    try {
      const start = await invoke<ChatStreamStart>('start_chat_stream', {
        request,
      });

      if (start.requestId !== request.requestId) {
        this.streamCallbacks.delete(request.requestId);
        throw new Error('Stream request ID mismatch.');
      }

      return start;
    } catch (error) {
      this.streamCallbacks.delete(request.requestId);
      throw error;
    }
  }

  async cancelChatStream(request: CancelChatStreamRequest): Promise<void> {
    await invoke('cancel_chat_stream', {
      request,
    });
  }

  async resolveAgentToolApproval(
    request: ResolveAgentToolApprovalRequest,
  ): Promise<void> {
    await invoke('resolve_agent_tool_approval', {
      request,
    });
  }

  async startBackgroundJob(
    request: StartBackgroundJobRequest,
  ): Promise<BackgroundJobSnapshot> {
    return invoke<BackgroundJobSnapshot>('start_background_job', {
      request,
    });
  }

  async getBackgroundJobStatus(jobId: string): Promise<BackgroundJobSnapshot> {
    return invoke<BackgroundJobSnapshot>('get_background_job_status', {
      jobId,
    });
  }

  async cancelBackgroundJob(request: CancelBackgroundJobRequest): Promise<void> {
    await invoke('cancel_background_job', {
      request,
    });
  }

  async dispose() {
    for (const unlisten of this.unlistenFns) {
      await unlisten();
    }
    this.unlistenFns = [];
    this.streamCallbacks.clear();
    this.listenersReady = null;
  }
}

export const terminalBridge = new TerminalBridge();
