import type { IO } from "../../../../../core/types";
import { doGitlabRequest, getProjectInfo } from "../../../../../utils/gitlab";
import { getProjectConfig } from "../../../../../config/getProjectConfig";
import { getGitRemoteHostAndPath } from "../../../../../git/gitProjectInformation";
import { getMainBranch } from "../../../../../git";
import type { AgentConfig } from "@catladder/pipeline";

type TriggerToken = {
  id: number;
  description: string;
  created_at: string;
  last_used: string | null;
  token: string;
  updated_at: string;
  owner: any;
};

type Webhook = {
  id: number;
  url: string;
  name: string;
  description: string;
  project_id: number;
  push_events: boolean;
  push_events_branch_filter: string;
  issues_events: boolean;
  confidential_issues_events: boolean;
  merge_requests_events: boolean;
  tag_push_events: boolean;
  note_events: boolean;
  confidential_note_events: boolean;
  job_events: boolean;
  pipeline_events: boolean;
  wiki_page_events: boolean;
  deployment_events: boolean;
  releases_events: boolean;
  milestone_events: boolean;
  feature_flag_events: boolean;
  enable_ssl_verification: boolean;
  repository_update_events: boolean;
  alert_status: string;
  disabled_until: string | null;
  url_variables: any[];
  created_at: string;
  resource_access_token_events: boolean;
  custom_webhook_template: string;
  custom_headers: Array<{ key: string; value?: string }>;
};

type WebhookData = {
  url: string;
  name: string;
  description: string;
  issues_events: boolean;
  confidential_issues_events: boolean;
  merge_requests_events: boolean;
  note_events: boolean;
  confidential_note_events: boolean;
  custom_webhook_template: string;
};

const listTriggerTokens = async (
  instance: IO,
  projectId: string,
): Promise<TriggerToken[]> => {
  return await doGitlabRequest(instance, `projects/${projectId}/triggers`);
};

const deleteTriggerToken = async (
  instance: IO,
  projectId: string,
  triggerId: number,
): Promise<void> => {
  await doGitlabRequest(
    instance,
    `projects/${projectId}/triggers/${triggerId}`,
    undefined,
    "DELETE",
  );
};

const createTriggerToken = async (
  instance: IO,
  projectId: string,
  description: string,
): Promise<TriggerToken> => {
  return await doGitlabRequest(
    instance,
    `projects/${projectId}/triggers`,
    { description },
    "POST",
  );
};

const listWebhooks = async (
  instance: IO,
  projectId: string,
): Promise<Webhook[]> => {
  return await doGitlabRequest(instance, `projects/${projectId}/hooks`);
};

const createWebhook = async (
  instance: IO,
  projectId: string,
  webhookData: WebhookData,
): Promise<Webhook> => {
  return await doGitlabRequest(
    instance,
    `projects/${projectId}/hooks`,
    webhookData,
    "POST",
  );
};

const updateWebhook = async (
  instance: IO,
  projectId: string,
  hookId: number,
  webhookData: WebhookData,
): Promise<Webhook> => {
  return await doGitlabRequest(
    instance,
    `projects/${projectId}/hooks/${hookId}`,
    webhookData,
    "PUT",
  );
};

const setupAgentWebhook = async (
  instance: IO,
  projectId: string,
  agentName: string,
  triggerToken: string,
): Promise<Webhook> => {
  const webhookName = `cl_agent_${agentName}_webhook`;
  const { gitRemoteHost } = await getGitRemoteHostAndPath();
  const mainBranch = await getMainBranch();
  const webhookUrl = `https://${gitRemoteHost}/api/v4/projects/${projectId}/ref/${mainBranch}/trigger/pipeline?token=${triggerToken}`;

  instance.log(`Setting up webhook for agent: ${agentName}`);

  // List existing webhooks
  const existingWebhooks = await listWebhooks(instance, projectId);

  // Find existing webhook by name
  const existingWebhook = existingWebhooks.find(
    (webhook) => webhook.name === webhookName,
  );

  // Custom webhook template from the user's specification
  const customWebhookTemplate = JSON.stringify({
    variables: {
      ASSIGNEE_USER_ID: "{{object_attributes.assignee_id}}",
      OBJECT_DESCRIPTION: "{{object_attributes.description}}",
    },
    payload: {
      object_kind: "{{object_kind}}",
      event_type: "{{event_type}}",
      user_id: "{{user.id}}",
      user_name: "{{user.name}}",
      user_username: "{{user.username}}",
      user_avatar_url: "{{user.avatar_url}}",
      user_email: "{{user.email}}",
      project_id: "{{project.id}}",
      project_name: "{{project.name}}",
      project_web_url: "{{project.web_url}}",
      project_namespace: "{{project.namespace}}",
      project_path_with_namespace: "{{project.path_with_namespace}}",
      object_attributes_id: "{{object_attributes.id}}",
      object_attributes_iid: "{{object_attributes.iid}}",
      object_attributes_title: "{{object_attributes.title}}",
      object_attributes_description: "{{object_attributes.description}}",
      object_attributes_state: "{{object_attributes.state}}",
      object_attributes_created_at: "{{object_attributes.created_at}}",
      object_attributes_updated_at: "{{object_attributes.updated_at}}",
      object_attributes_url: "{{object_attributes.url}}",
      object_attributes_action: "{{object_attributes.action}}",
      object_attributes_noteable_type: "{{object_attributes.noteable_type}}",
      merge_request_id: "{{merge_request.id}}",
      merge_request_iid: "{{merge_request.iid}}",
      merge_request_title: "{{merge_request.title}}",
      merge_request_source_branch: "{{merge_request.source_branch}}",
      merge_request_time_estimate: "{{merge_request.time_estimate}}",
      merge_request_description: "{{merge_request.description}}",
      merge_request_merge_status: "{{merge_request.merge_status}}",
      merge_request_last_commit_title: "{{merge_request.last_commit.title}}",
      merge_request_last_commit_message:
        "{{merge_request.last_commit.message}}",
      merge_request_last_commit_author_name:
        "{{merge_request.last_commit.author.name}}",
      merge_request_last_commit_author_email:
        "{{merge_request.last_commit.author.email}}",
    },
  });

  const webhookData: WebhookData = {
    url: webhookUrl,
    name: webhookName,
    description: `Agent webhook for ${agentName} - triggers pipeline with custom payload`,
    issues_events: true,
    confidential_issues_events: false,
    merge_requests_events: false,
    note_events: true,
    confidential_note_events: false,
    custom_webhook_template: customWebhookTemplate,
  };

  let webhook: Webhook;

  if (existingWebhook) {
    instance.log(
      `Updating existing webhook: ${webhookName} (ID: ${existingWebhook.id})`,
    );
    webhook = await updateWebhook(
      instance,
      projectId,
      existingWebhook.id,
      webhookData,
    );
    instance.log(`✅ Updated webhook: ${webhook.name}`);
  } else {
    instance.log(`Creating new webhook: ${webhookName}`);
    webhook = await createWebhook(instance, projectId, webhookData);
    instance.log(`✅ Created webhook: ${webhook.name}`);
  }

  instance.log(`   Webhook ID: ${webhook.id}`);
  instance.log(`   Webhook URL: ${webhook.url}`);

  return webhook;
};

const setupAgentTriggerToken = async (
  instance: IO,
  projectId: string,
  agentName: string,
): Promise<TriggerToken> => {
  const tokenName = `cl_agent_${agentName}`;

  instance.log(`Setting up trigger token for agent: ${agentName}`);

  // List existing trigger tokens
  const existingTokens = await listTriggerTokens(instance, projectId);

  // Find and delete existing cl_agent_<agentName> tokens
  const existingAgentTokens = existingTokens.filter((token) =>
    token.description.startsWith(`cl_agent_${agentName}`),
  );

  for (const token of existingAgentTokens) {
    instance.log(
      `Deleting existing trigger token: ${token.description} (ID: ${token.id})`,
    );
    await deleteTriggerToken(instance, projectId, token.id);
  }

  // Create new trigger token
  const description = `${tokenName} - Agent trigger token for ${agentName}`;
  const newToken = await createTriggerToken(instance, projectId, description);

  instance.log(`✅ Created new trigger token: ${newToken.description}`);
  instance.log(`   Token ID: ${newToken.id}`);
  instance.log(`   Token: ${newToken.token}`);

  return newToken;
};

const setupAgent = async (
  instance: IO,
  agentName: string,
  agentConfig: AgentConfig,
) => {
  const { id: projectId } = await getProjectInfo(instance);

  instance.log(`🤖 Setting up agent: ${agentName}`);

  // Setup trigger token for this agent
  const triggerToken = await setupAgentTriggerToken(
    instance,
    projectId,
    agentName,
  );

  // Setup webhook for this agent
  await setupAgentWebhook(instance, projectId, agentName, triggerToken.token);

  instance.log(`✅ Agent ${agentName} setup complete!`);
};

export const setupAgents = async (instance: IO) => {
  const config = await getProjectConfig();

  instance.log("");
  instance.log("🤖 Setting up agents...");

  const agents = config.agents ?? {};
  const agentEntries = Object.entries(agents);

  if (agentEntries.length === 0) {
    instance.log("No agents configured in project config");
    return;
  }

  for (const [agentName, agentConfig] of agentEntries) {
    if (agentConfig.type === "claude" && agentConfig) {
      await setupAgent(instance, agentName, agentConfig);
    }
  }

  instance.log("✅ All agents setup complete!");
  instance.log("");
  instance.log("🔧 Manual setup required:");
  instance.log(
    "Please configure these environment variables in GitLab CI/CD unless they are already set (by group or global):",
  );
  instance.log("   Go to Settings > CI/CD > Variables and add:");
  instance.log(
    "   - CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY (your Anthropic API key, mark as Protected & Masked)",
  );
  instance.log(
    "   - AGENT_GITLAB_PERSONAL_ACCESS_TOKEN (token from agent.claude user, mark as Protected & Masked)",
  );
};
