/**
 * HTTP request/response gateways for the web transport — ticket #13, lot F1.
 *
 * Each class implements a UI port by forwarding the **exact** backend command
 * name + camelCase argument envelope its Tauri sibling uses (the shared backend
 * core, so identical contracts) through the generic {@link HttpInvoker}. No DTO
 * shape is re-derived here; only the transport changes (Tauri `invoke` → HTTP
 * `POST /api/invoke`). Normalizers that are transport-neutral (`workState`,
 * `conversation`) are reused from the sibling adapters.
 *
 * Lives in `src/adapters/**`; touches no `@tauri-apps/api`.
 */

import type {
  Agent,
  AgentDrift,
  AgentProfile,
  EmbedderEngines,
  EmbedderProfile,
  FirstRunState,
  GitBranches,
  GitCommit,
  GitFileStatus,
  GraphCommit,
  LayoutKind,
  LayoutList,
  LayoutOperation,
  LayoutTree,
  PluginLayoutOrigin,
  LocalModelServerConfig,
  Memory,
  MemoryIndexEntry,
  MemoryLink,
  MemoryType,
  McpToolPolicy,
  ModelServerCommandPreview,
  OpenCodeProviderCatalogEntry,
  PermissionSet,
  Project,
  ProjectMcpToolPermissions,
  ProjectPermissions,
  ProjectWorkState,
  ProjectSystemPermissions,
  ProfileAvailability,
  ProfileModelCatalog,
  ResolvedAgentPermissions,
  ResolvedAgentSystemPermissions,
  SystemPermissionSet,
  Skill,
  SkillScope,
  Template,
  TurnPage,
} from "@/domain";
import type {
  CloneProfileFromSeedInput,
  CloneOpenCodeProfileFromSeedInput,
  ConversationGateway,
  ConversationPageRequest,
  CreateMemoryInput,
  CreateSkillInput,
  CreateTemplateInput,
  EmbedderGateway,
  GitGateway,
  InputGateway,
  LayoutGateway,
  MemoryGateway,
  ModelServerGateway,
  PermissionGateway,
  ProfileGateway,
  ProjectGateway,
  SaveOpenCodeProviderProfileInput,
  SkillGateway,
  TemplateGateway,
  WorkStateGateway,
  BackgroundTaskAttachment,
} from "@/ports";
import { normalizeProjectWorkState } from "../workStateNormalization";
import { unsupportedOnWeb } from "./unsupported";
import { normalizeTurnPage } from "../conversationNormalization";
import { normalizeProfileModelCatalog } from "../profileCatalog";
import type { HttpInvoker } from "./httpInvoker";

export class HttpProjectGateway implements ProjectGateway {
  constructor(private readonly http: HttpInvoker) {}
  listProjects(): Promise<Project[]> {
    return this.http.invoke<Project[]>("list_projects");
  }
  createProject(name: string, root: string): Promise<Project> {
    return this.http.invoke<Project>("create_project", { request: { name, root } });
  }
  openProject(projectId: string): Promise<Project> {
    return this.http.invoke<Project>("open_project", { projectId });
  }
  async closeProject(projectId: string): Promise<void> {
    await this.http.invoke("close_project", { projectId });
  }
  readProjectContext(projectId: string): Promise<string> {
    return this.http.invoke<string>("read_project_context", { projectId });
  }
  async updateProjectContext(projectId: string, content: string): Promise<void> {
    await this.http.invoke("update_project_context", { request: { projectId, content } });
  }
}

export class HttpLayoutGateway implements LayoutGateway {
  constructor(private readonly http: HttpInvoker) {}
  loadLayout(projectId: string, layoutId?: string): Promise<LayoutTree> {
    return this.http.invoke<LayoutTree>("load_layout", { projectId, layoutId });
  }
  mutateLayout(
    projectId: string,
    operation: LayoutOperation,
    layoutId?: string,
  ): Promise<LayoutTree> {
    return this.http.invoke<LayoutTree>("mutate_layout", { projectId, layoutId, operation });
  }
  listLayouts(projectId: string): Promise<LayoutList> {
    return this.http.invoke<LayoutList>("list_layouts", { projectId });
  }
  createLayout(
    projectId: string,
    name: string,
    kind?: LayoutKind,
    pluginOrigin?: PluginLayoutOrigin,
  ): Promise<{ layoutId: string }> {
    return this.http.invoke<{ layoutId: string }>("create_layout", {
      request: { projectId, name, kind, pluginOrigin },
    });
  }
  renameLayout(projectId: string, layoutId: string, name: string): Promise<void> {
    return this.http.invoke<void>("rename_layout", { request: { projectId, layoutId, name } });
  }
  deleteLayout(projectId: string, layoutId: string): Promise<{ activeId: string }> {
    return this.http.invoke<{ activeId: string }>("delete_layout", { request: { projectId, layoutId } });
  }
  setActiveLayout(projectId: string, layoutId: string): Promise<{ activeId: string }> {
    return this.http.invoke<{ activeId: string }>("set_active_layout", { request: { projectId, layoutId } });
  }
}

export class HttpGitGateway implements GitGateway {
  constructor(private readonly http: HttpInvoker) {}
  status(projectId: string): Promise<GitFileStatus[]> {
    return this.http.invoke<GitFileStatus[]>("git_status", { projectId });
  }
  async stage(projectId: string, path: string): Promise<void> {
    await this.http.invoke("git_stage", { request: { projectId, path } });
  }
  async unstage(projectId: string, path: string): Promise<void> {
    await this.http.invoke("git_unstage", { request: { projectId, path } });
  }
  commit(projectId: string, message: string): Promise<GitCommit> {
    return this.http.invoke<GitCommit>("git_commit", { request: { projectId, message } });
  }
  branches(projectId: string): Promise<GitBranches> {
    return this.http.invoke<GitBranches>("git_branches", { projectId });
  }
  async checkout(projectId: string, branch: string): Promise<void> {
    await this.http.invoke("git_checkout", { request: { projectId, branch } });
  }
  log(projectId: string, limit: number): Promise<GitCommit[]> {
    return this.http.invoke<GitCommit[]>("git_log", { projectId, limit });
  }
  async init(projectId: string): Promise<void> {
    await this.http.invoke("git_init", { projectId });
  }
  graph(projectId: string, limit: number): Promise<GraphCommit[]> {
    return this.http.invoke<GraphCommit[]>("git_graph", { projectId, limit });
  }
}

export class HttpProfileGateway implements ProfileGateway {
  constructor(private readonly http: HttpInvoker) {}
  firstRunState(): Promise<FirstRunState> {
    return this.http.invoke<FirstRunState>("first_run_state");
  }
  referenceProfiles(): Promise<AgentProfile[]> {
    return this.http.invoke<AgentProfile[]>("reference_profiles");
  }
  detectProfiles(candidates: AgentProfile[]): Promise<ProfileAvailability[]> {
    return this.http.invoke<ProfileAvailability[]>("detect_profiles", { request: { candidates } });
  }
  listProfiles(): Promise<AgentProfile[]> {
    return this.http.invoke<AgentProfile[]>("list_profiles");
  }
  saveProfile(profile: AgentProfile): Promise<AgentProfile> {
    return this.http.invoke<AgentProfile>("save_profile", { request: { profile } });
  }
  async deleteProfile(profileId: string): Promise<void> {
    await this.http.invoke("delete_profile", { profileId });
  }
  cloneProfileFromSeed(input: CloneProfileFromSeedInput): Promise<AgentProfile> {
    return this.http.invoke<AgentProfile>("clone_profile_from_seed", {
      request: { seedProfileId: input.seedProfileId, name: input.name, model: input.model },
    });
  }
  async listClaudeModels(): Promise<ProfileModelCatalog> {
    return normalizeProfileModelCatalog(await this.http.invoke<unknown>("list_claude_models"));
  }
  async listCodexModels(): Promise<ProfileModelCatalog> {
    return normalizeProfileModelCatalog(await this.http.invoke<unknown>("list_codex_models"));
  }
  configureProfiles(profiles: AgentProfile[]): Promise<AgentProfile[]> {
    return this.http.invoke<AgentProfile[]>("configure_profiles", { request: { profiles } });
  }
  cloneOpenCodeProfileFromSeed(
    input: CloneOpenCodeProfileFromSeedInput = {},
  ): Promise<AgentProfile> {
    return this.http.invoke<AgentProfile>("clone_opencode_profile_from_seed", {
      request: { name: input.name, opencode: input.opencode },
    });
  }
  listOpenCodeProviders(): Promise<OpenCodeProviderCatalogEntry[]> {
    return this.http.invoke<OpenCodeProviderCatalogEntry[]>("list_opencode_providers");
  }
  saveOpenCodeProviderProfile(
    input: SaveOpenCodeProviderProfileInput,
  ): Promise<AgentProfile> {
    return this.http.invoke<AgentProfile>("save_opencode_provider_profile", {
      request: {
        profile: input.profile,
        providerId: input.providerId,
        model: input.model,
        apiKey: input.apiKey,
        custom: input.custom,
      },
    });
  }
}

export class HttpModelServerGateway implements ModelServerGateway {
  constructor(private readonly http: HttpInvoker) {}
  listModelServers(): Promise<LocalModelServerConfig[]> {
    return this.http.invoke<LocalModelServerConfig[]>("list_model_servers");
  }
  saveModelServer(config: LocalModelServerConfig): Promise<LocalModelServerConfig> {
    return this.http.invoke<LocalModelServerConfig>("save_model_server", { request: { config } });
  }
  async deleteModelServer(serverId: string): Promise<void> {
    await this.http.invoke("delete_model_server", { serverId });
  }
  async deleteModelArtifact(serverId: string): Promise<void> {
    await this.http.invoke("delete_model_artifact", { serverId });
  }
  previewModelServerCommand(config: LocalModelServerConfig): Promise<ModelServerCommandPreview> {
    return this.http.invoke<ModelServerCommandPreview>("preview_model_server_command", { config });
  }
}

export class HttpTemplateGateway implements TemplateGateway {
  constructor(private readonly http: HttpInvoker) {}
  listTemplates(): Promise<Template[]> {
    return this.http.invoke<Template[]>("list_templates");
  }
  createTemplate(input: CreateTemplateInput): Promise<Template> {
    return this.http.invoke<Template>("create_template", {
      request: { name: input.name, content: input.content, defaultProfileId: input.defaultProfileId },
    });
  }
  updateTemplate(templateId: string, content: string): Promise<Template> {
    return this.http.invoke<Template>("update_template", { request: { templateId, content } });
  }
  async deleteTemplate(templateId: string): Promise<void> {
    await this.http.invoke("delete_template", { templateId });
  }
  createAgentFromTemplate(
    projectId: string,
    templateId: string,
    opts?: { name?: string; synchronized?: boolean },
  ): Promise<Agent> {
    return this.http.invoke<Agent>("create_agent_from_template", {
      request: {
        projectId,
        templateId,
        name: opts?.name ?? null,
        synchronized: opts?.synchronized ?? true,
      },
    });
  }
  detectDrift(projectId: string): Promise<AgentDrift[]> {
    return this.http.invoke<AgentDrift[]>("detect_agent_drift", { projectId });
  }
  syncAgent(projectId: string, agentId: string): Promise<{ synced: boolean; version: number | null }> {
    return this.http.invoke<{ synced: boolean; version: number | null }>("sync_agent_with_template", {
      request: { projectId, agentId },
    });
  }
}

export class HttpSkillGateway implements SkillGateway {
  constructor(private readonly http: HttpInvoker) {}
  listSkills(projectId: string, scope: SkillScope): Promise<Skill[]> {
    return this.http.invoke<Skill[]>("list_skills", { projectId, scope });
  }
  createSkill(input: CreateSkillInput): Promise<Skill> {
    return this.http.invoke<Skill>("create_skill", {
      request: {
        projectId: input.projectId,
        name: input.name,
        description: input.description,
        kind: input.kind,
        content: input.content,
        scope: input.scope,
      },
    });
  }
  updateSkill(projectId: string, scope: SkillScope, skillId: string, content: string): Promise<Skill> {
    return this.http.invoke<Skill>("update_skill", { request: { projectId, scope, skillId, content } });
  }
  async deleteSkill(projectId: string, scope: SkillScope, skillId: string): Promise<void> {
    await this.http.invoke("delete_skill", { projectId, scope, skillId });
  }
  async assignSkill(projectId: string, agentId: string, skillId: string, scope: SkillScope): Promise<void> {
    await this.http.invoke("assign_skill_to_agent", { request: { projectId, agentId, skillId, scope } });
  }
  async unassignSkill(projectId: string, agentId: string, skillId: string): Promise<void> {
    await this.http.invoke("unassign_skill_from_agent", { request: { projectId, agentId, skillId } });
  }
}

export class HttpMemoryGateway implements MemoryGateway {
  constructor(private readonly http: HttpInvoker) {}
  listMemories(projectId: string): Promise<Memory[]> {
    return this.http.invoke<Memory[]>("list_memories", { projectId });
  }
  getMemory(projectId: string, slug: string): Promise<Memory> {
    return this.http.invoke<Memory>("get_memory", { projectId, slug });
  }
  createMemory(input: CreateMemoryInput): Promise<Memory> {
    return this.http.invoke<Memory>("create_memory", {
      request: {
        projectId: input.projectId,
        name: input.name,
        description: input.description,
        type: input.type,
        content: input.content,
      },
    });
  }
  updateMemory(
    projectId: string,
    slug: string,
    description: string,
    type: MemoryType,
    content: string,
  ): Promise<Memory> {
    return this.http.invoke<Memory>("update_memory", { request: { projectId, slug, description, type, content } });
  }
  async deleteMemory(projectId: string, slug: string): Promise<void> {
    await this.http.invoke("delete_memory", { projectId, slug });
  }
  readIndex(projectId: string): Promise<MemoryIndexEntry[]> {
    return this.http.invoke<MemoryIndexEntry[]>("read_memory_index", { projectId });
  }
  resolveLinks(projectId: string, slug: string): Promise<MemoryLink[]> {
    return this.http.invoke<MemoryLink[]>("resolve_memory_links", { projectId, slug });
  }
  recall(projectId: string, text: string, tokenBudget: number): Promise<MemoryIndexEntry[]> {
    return this.http.invoke<MemoryIndexEntry[]>("recall_memory", { request: { projectId, text, tokenBudget } });
  }
}

export class HttpEmbedderGateway implements EmbedderGateway {
  constructor(private readonly http: HttpInvoker) {}
  listEmbedderProfiles(): Promise<EmbedderProfile[]> {
    return this.http.invoke<EmbedderProfile[]>("list_embedder_profiles");
  }
  saveEmbedderProfile(profile: EmbedderProfile): Promise<EmbedderProfile> {
    return this.http.invoke<EmbedderProfile>("save_embedder_profile", { request: { profile } });
  }
  async deleteEmbedderProfile(embedderId: string): Promise<void> {
    await this.http.invoke("delete_embedder_profile", { embedderId });
  }
  describeEmbedderEngines(): Promise<EmbedderEngines> {
    return this.http.invoke<EmbedderEngines>("describe_embedder_engines");
  }
}

export class HttpPermissionGateway implements PermissionGateway {
  constructor(private readonly http: HttpInvoker) {}
  getProjectPermissions(projectId: string): Promise<ProjectPermissions> {
    return this.http.invoke<ProjectPermissions>("get_project_permissions", { projectId });
  }
  updateProjectPermissions(
    projectId: string,
    permissions: PermissionSet | null,
  ): Promise<ProjectPermissions> {
    return this.http.invoke<ProjectPermissions>("update_project_permissions", { request: { projectId, permissions } });
  }
  updateAgentPermissions(
    projectId: string,
    agentId: string,
    permissions: PermissionSet | null,
  ): Promise<ProjectPermissions> {
    return this.http.invoke<ProjectPermissions>("update_agent_permissions", {
      request: { projectId, agentId, permissions },
    });
  }
  resolveAgentPermissions(projectId: string, agentId: string): Promise<ResolvedAgentPermissions> {
    return this.http.invoke<ResolvedAgentPermissions>("resolve_agent_permissions", {
      request: { projectId, agentId },
    });
  }
  getProjectSystemPermissions(projectId: string): Promise<ProjectSystemPermissions> {
    return this.http.invoke<ProjectSystemPermissions>("get_project_system_permissions", {
      projectId,
    });
  }
  updateProjectSystemPermissions(
    projectId: string,
    permissions: SystemPermissionSet | null,
  ): Promise<ProjectSystemPermissions> {
    return this.http.invoke<ProjectSystemPermissions>("update_project_system_permissions", {
      request: { projectId, permissions },
    });
  }
  updateAgentSystemPermissions(
    projectId: string,
    agentId: string,
    permissions: SystemPermissionSet | null,
  ): Promise<ProjectSystemPermissions> {
    return this.http.invoke<ProjectSystemPermissions>("update_agent_system_permissions", {
      request: { projectId, agentId, permissions },
    });
  }
  resolveAgentSystemPermissions(
    projectId: string,
    agentId: string,
  ): Promise<ResolvedAgentSystemPermissions> {
    return this.http.invoke<ResolvedAgentSystemPermissions>(
      "resolve_agent_system_permissions",
      { request: { projectId, agentId } },
    );
  }
  getMcpToolPermissions(projectId: string): Promise<ProjectMcpToolPermissions> {
    return this.http.invoke<ProjectMcpToolPermissions>("get_mcp_tool_permissions", { projectId });
  }
  updateProjectMcpToolPermissions(
    projectId: string,
    policy: McpToolPolicy | null,
  ): Promise<ProjectMcpToolPermissions> {
    return this.http.invoke<ProjectMcpToolPermissions>("update_project_mcp_tool_permissions", {
      request: { projectId, policy },
    });
  }
  updateAgentMcpToolPermissions(
    projectId: string,
    agentId: string,
    policy: McpToolPolicy | null,
  ): Promise<ProjectMcpToolPermissions> {
    return this.http.invoke<ProjectMcpToolPermissions>("update_agent_mcp_tool_permissions", {
      request: { projectId, agentId, policy },
    });
  }
}

export class HttpWorkStateGateway implements WorkStateGateway {
  constructor(private readonly http: HttpInvoker) {}
  async getProjectWorkState(projectId: string): Promise<ProjectWorkState> {
    const state = await this.http.invoke<unknown>("get_project_work_state", { projectId });
    return normalizeProjectWorkState(state);
  }
  async attachBackgroundTask(
    _taskId: string,
    _onData: (bytes: Uint8Array) => void,
  ): Promise<BackgroundTaskAttachment> {
    return unsupportedOnWeb("Attaching to a background task live stream");
  }
  async cancelBackgroundTask(taskId: string): Promise<void> {
    await this.http.invoke<unknown>("cancel_background_task", { taskId });
  }
  async retryBackgroundTask(taskId: string): Promise<void> {
    await this.http.invoke<unknown>("retry_background_task", { taskId });
  }
}

export class HttpConversationGateway implements ConversationGateway {
  constructor(private readonly http: HttpInvoker) {}
  async readPage(
    projectId: string,
    conversationId: string,
    request?: ConversationPageRequest,
  ): Promise<TurnPage> {
    const page = await this.http.invoke<unknown>("read_conversation_page", {
      request: {
        projectId,
        conversationId,
        anchor: request?.anchor,
        direction: request?.direction ?? "backward",
        limit: request?.limit,
      },
    });
    return normalizeTurnPage(page);
  }
}

export class HttpInputGateway implements InputGateway {
  constructor(private readonly http: HttpInvoker) {}
  async interrupt(projectId: string, agentId: string): Promise<void> {
    await this.http.invoke("interrupt_agent", { request: { projectId, agentId } });
  }
  async delegationDelivered(projectId: string, agentId: string, ticket: string): Promise<void> {
    await this.http.invoke("delegation_delivered", { request: { projectId, agentId, ticket } });
  }
  async setFrontAttached(agentId: string, attached: boolean): Promise<void> {
    await this.http.invoke("set_front_attached", { request: { agentId, attached } });
  }
  async cancelResume(agentId: string): Promise<boolean> {
    return this.http.invoke<boolean>("cancel_resume", { agentId });
  }
  async setResumeAt(agentId: string, resetsAtMs: number): Promise<void> {
    await this.http.invoke("set_resume_at", { agentId, resetsAtMs });
  }
}
