const PROJECT_BLOCK_SPLIT_REGEX = /(?=\[\[projects\]\])/;
const PROJECT_NAME_REGEX = /name\s*=\s*"([^"]+)"/;
const PROJECT_AGENT_TYPE_REGEX = /\[projects\.agent\]\s*\ntype\s*=\s*"([^"]+)"/;
const PROJECT_WORK_DIR_REGEX = /\[projects\.agent\.options\][\s\S]*?work_dir\s*=\s*"([^"]*)"/;
const WEIXIN_OPTIONS_SECTION_REGEX = /(\[\[projects\.platforms\]\]\s*\ntype\s*=\s*"weixin"\s*\n\s*\n\[projects\.platforms\.options\]\s*\n)([\s\S]*?)(?=\n\[\[projects\]\]|$)/;

const OPTION_LINE_REGEX = /^([a-zA-Z0-9_]+)\s*=\s*"((?:[^"\\]|\\.)*)"$/gm;

export interface AxhubWeixinState<TAgent extends string = string> {
  version: 1;
  configuredAgents: TAgent[];
  activeAgent: TAgent;
  workDir: string;
  weixinOptions: Record<string, string>;
}

interface ParsedProjectBlock {
  name: string | null;
  agentType: string | null;
  workDir: string | null;
  weixinOptions: Record<string, string> | null;
}

function unescapeTomlString(value: string): string {
  return value
    .replace(/\\\\/g, '\\')
    .replace(/\\"/g, '"')
    .replace(/\\n/g, '\n')
    .replace(/\\r/g, '\r')
    .replace(/\\t/g, '\t');
}

function escapeTomlString(value: string): string {
  return value
    .replace(/\\/g, '\\\\')
    .replace(/"/g, '\\"')
    .replace(/\n/g, '\\n')
    .replace(/\r/g, '\\r')
    .replace(/\t/g, '\\t');
}

function parseTomlOptionsBody(body: string): Record<string, string> {
  const options: Record<string, string> = {};
  for (const match of body.matchAll(OPTION_LINE_REGEX)) {
    options[match[1]] = unescapeTomlString(match[2]);
  }
  return options;
}

function parseProjectBlocks(content: string): ParsedProjectBlock[] {
  return content
    .split(PROJECT_BLOCK_SPLIT_REGEX)
    .filter((block) => block.includes('[[projects]]'))
    .map((block) => {
      const weixinMatch = block.match(WEIXIN_OPTIONS_SECTION_REGEX);
      return {
        name: block.match(PROJECT_NAME_REGEX)?.[1] || null,
        agentType: block.match(PROJECT_AGENT_TYPE_REGEX)?.[1] || null,
        workDir: block.match(PROJECT_WORK_DIR_REGEX)?.[1] || null,
        weixinOptions: weixinMatch ? parseTomlOptionsBody(weixinMatch[2]) : null,
      };
    });
}

function getPreferredOptionKeys(options: Record<string, string>): string[] {
  const preferred = ['token', 'base_url', 'account_id', 'allow_from', 'admin_from'];
  const remaining = Object.keys(options)
    .filter((key) => !preferred.includes(key))
    .sort();
  return [...preferred.filter((key) => key in options), ...remaining];
}

function serializeTomlOptions(options: Record<string, string>): string {
  return getPreferredOptionKeys(options)
    .map((key) => `${key} = "${escapeTomlString(options[key])}"`)
    .join('\n');
}

function buildHeader(): string {
  return `# Auto-generated by Axhub Make — cc-connect WeChat integration
# language = "zh"

[log]
level = "info"

`;
}

export function findActiveAgentFromConfig<TAgent extends string>(params: {
  content: string;
  supportedAgents: TAgent[];
  blockedUserId: string;
}): TAgent | null {
  const { content, supportedAgents, blockedUserId } = params;
  const supportedAgentSet = new Set<string>(supportedAgents);

  for (const project of parseProjectBlocks(content)) {
    if (!project.agentType || !supportedAgentSet.has(project.agentType)) continue;
    const allowFrom = project.weixinOptions?.allow_from;
    if (allowFrom && allowFrom !== blockedUserId && allowFrom.includes('@im.wechat')) {
      return project.agentType as TAgent;
    }
  }

  return null;
}

export function createAxhubWeixinStateFromConfig<TAgent extends string>(params: {
  content: string;
  supportedAgents: TAgent[];
  blockedUserId: string;
  preferredActiveAgent?: TAgent | null;
  recoveredAllowFrom?: string | null;
}): AxhubWeixinState<TAgent> | null {
  const {
    content,
    supportedAgents,
    blockedUserId,
    preferredActiveAgent = null,
    recoveredAllowFrom = null,
  } = params;

  const supportedAgentSet = new Set<string>(supportedAgents);
  const projects = parseProjectBlocks(content).filter((project) => {
    return (
      !!project.agentType &&
      supportedAgentSet.has(project.agentType) &&
      !!project.weixinOptions?.token
    );
  });

  if (projects.length === 0) {
    return null;
  }

  const configuredAgents = supportedAgents.filter((agent) => {
    return projects.some((project) => project.agentType === agent);
  });

  if (configuredAgents.length === 0) {
    return null;
  }

  const activeAgentFromConfig = findActiveAgentFromConfig({
    content,
    supportedAgents: configuredAgents,
    blockedUserId,
  });

  const activeAgent = activeAgentFromConfig
    || (preferredActiveAgent && configuredAgents.includes(preferredActiveAgent) ? preferredActiveAgent : null)
    || configuredAgents[0];

  const activeProject = projects.find((project) => project.agentType === activeAgent) || projects[0];
  const allowFrom = activeProject.weixinOptions?.allow_from
    && activeProject.weixinOptions.allow_from !== blockedUserId
    && activeProject.weixinOptions.allow_from.includes('@im.wechat')
    ? activeProject.weixinOptions.allow_from
    : recoveredAllowFrom;

  if (!allowFrom) {
    return null;
  }

  return {
    version: 1,
    configuredAgents,
    activeAgent,
    workDir: activeProject.workDir || '',
    weixinOptions: {
      ...(activeProject.weixinOptions || {}),
      allow_from: allowFrom,
    },
  };
}

export function generateSingleAgentConfigToml<TAgent extends string>(params: {
  agent: TAgent;
  workDir: string;
  weixinOptions: Record<string, string>;
}): string {
  const { agent, workDir, weixinOptions } = params;
  return `${buildHeader()}[[projects]]
name = "axhub-${escapeTomlString(agent)}"

[projects.agent]
type = "${escapeTomlString(agent)}"

[projects.agent.options]
work_dir = "${escapeTomlString(workDir)}"
mode = "yolo"

[[projects.platforms]]
type = "weixin"

[projects.platforms.options]
${serializeTomlOptions(weixinOptions)}
`;
}

export function generateWeixinSetupConfigToml<TAgent extends string>(params: {
  agent: TAgent;
  workDir: string;
}): string {
  const { agent, workDir } = params;
  return generateSingleAgentConfigToml({
    agent,
    workDir,
    weixinOptions: {
      token: '',
    },
  });
}
