import { getWordstatRuntimeConfig, type WordstatRuntimeConfig } from "../settings/externalServiceSettings.js";

export type WordstatSeedInput = {
  seedId: string;
  phrase: string;
  clusterId: string;
  clusterTitle: string;
  priority: "high" | "medium" | "low";
  source: "detected" | "ontology";
};

export type WordstatCollectRequest = {
  projectId: string;
  semanticRunId: string;
  region: string;
  seeds: WordstatSeedInput[];
};

export type WordstatRelatedQuery = {
  phrase: string;
  frequency: number | null;
  frequencyGroup: string | null;
  raw: unknown;
};

export type WordstatSeedResult = {
  seedId: string;
  seedPhrase: string;
  frequency: number | null;
  frequencyGroup: string | null;
  relatedQueries: WordstatRelatedQuery[];
  raw: unknown;
};

export type WordstatCollectResult = {
  provider: "mcp_kv" | "yandex_api";
  mode: string;
  region: string;
  collectedAt: string;
  seedResults: WordstatSeedResult[];
  raw: unknown;
};

export type WordstatProviderStatus =
  | {
      connected: true;
      provider: "mcp_kv" | "yandex_api";
      mode: string;
      message: string;
    }
  | {
      connected: false;
      provider: "disabled" | "mcp_kv" | "yandex_api";
      mode: string;
      message: string;
    };

export interface WordstatProvider {
  getStatus(): WordstatProviderStatus;
  collect(request: WordstatCollectRequest): Promise<WordstatCollectResult>;
}

export class WordstatProviderUnavailableError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "WordstatProviderUnavailableError";
  }
}

function getFrequencyGroup(frequency: number | null, explicitGroup?: unknown) {
  if (typeof explicitGroup === "string" && explicitGroup.trim()) {
    return explicitGroup.trim();
  }

  if (frequency === null) {
    return null;
  }

  if (frequency >= 10_000) {
    return "high";
  }

  if (frequency >= 1_000) {
    return "mid";
  }

  return "low";
}

function getString(value: unknown) {
  return typeof value === "string" ? value.trim() : "";
}

function getNumber(value: unknown) {
  if (typeof value === "number" && Number.isFinite(value)) {
    return Math.max(0, Math.round(value));
  }

  if (typeof value === "string") {
    const normalized = Number(value.replace(/\s+/g, ""));

    if (Number.isFinite(normalized)) {
      return Math.max(0, Math.round(normalized));
    }
  }

  return null;
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
}

function asArray(value: unknown) {
  return Array.isArray(value) ? value : [];
}

function normalizeRelatedQuery(value: unknown): WordstatRelatedQuery | null {
  const record = asRecord(value);
  const phrase =
    getString(record.phrase) ||
    getString(record.query) ||
    getString(record.keyword) ||
    getString(record.text) ||
    getString(record.name);

  if (!phrase) {
    return null;
  }

  const frequency =
    getNumber(record.frequency) ?? getNumber(record.shows) ?? getNumber(record.count) ?? getNumber(record.impressions);

  return {
    phrase,
    frequency,
    frequencyGroup: getFrequencyGroup(frequency, record.frequencyGroup ?? record.frequency_group ?? record.group),
    raw: value
  };
}

function normalizeSeedResult(value: unknown, seed: WordstatSeedInput): WordstatSeedResult {
  const record = asRecord(value);
  const phrase =
    getString(record.seedPhrase) ||
    getString(record.seed_phrase) ||
    getString(record.phrase) ||
    getString(record.query) ||
    seed.phrase;
  const frequency =
    getNumber(record.frequency) ??
    getNumber(record.shows) ??
    getNumber(record.count) ??
    getNumber(record.total) ??
    getNumber(record.impressions);
  const relatedSource =
    record.relatedQueries ??
    record.related_queries ??
    record.queries ??
    record.items ??
    record.results ??
    record.associations;

  return {
    seedId: seed.seedId,
    seedPhrase: phrase,
    frequency,
    frequencyGroup: getFrequencyGroup(frequency, record.frequencyGroup ?? record.frequency_group ?? record.group),
    relatedQueries: asArray(relatedSource).flatMap((related) => {
      const normalized = normalizeRelatedQuery(related);
      return normalized ? [normalized] : [];
    }),
    raw: value
  };
}

function normalizeProviderResponse(
  provider: "mcp_kv" | "yandex_api",
  mode: string,
  region: string,
  seeds: WordstatSeedInput[],
  payload: unknown
): WordstatCollectResult {
  const record = asRecord(payload);
  const resultSource = record.results ?? record.seedResults ?? record.seed_results ?? record.data ?? record.items;
  const resultRecords = asArray(resultSource);
  const resultByPhrase = new Map<string, unknown>();

  for (const resultRecord of resultRecords) {
    const result = asRecord(resultRecord);
    const phrase =
      getString(result.seedPhrase) ||
      getString(result.seed_phrase) ||
      getString(result.phrase) ||
      getString(result.query) ||
      getString(result.keyword);

    if (phrase) {
      resultByPhrase.set(phrase.toLowerCase(), resultRecord);
    }
  }

  return {
    provider,
    mode,
    region,
    collectedAt: new Date().toISOString(),
    seedResults: seeds.map((seed, index) =>
      normalizeSeedResult(resultByPhrase.get(seed.phrase.toLowerCase()) ?? resultRecords[index] ?? {}, seed)
    ),
    raw: payload
  };
}

class DisabledWordstatProvider implements WordstatProvider {
  getStatus(): WordstatProviderStatus {
    return {
      connected: false,
      provider: "disabled",
      mode: "disabled",
      message: "Wordstat provider отключён на платформе."
    };
  }

  async collect(): Promise<WordstatCollectResult> {
    throw new WordstatProviderUnavailableError("Wordstat provider отключён на платформе.");
  }
}

async function postJson(endpoint: string, body: unknown, headers: Record<string, string> = {}) {
  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      ...headers
    },
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    const message = await response.text().catch(() => "");
    throw new Error(`Wordstat provider вернул ${response.status}${message ? `: ${message.slice(0, 240)}` : ""}`);
  }

  return response.json() as Promise<unknown>;
}

class McpKvWordstatProvider implements WordstatProvider {
  constructor(private readonly endpoint: string | undefined) {}

  getStatus(): WordstatProviderStatus {
    if (!this.endpoint) {
      return {
        connected: false,
        provider: "mcp_kv",
        mode: "mcp_kv",
        message: "WORDSTAT_MCP_ENDPOINT не настроен."
      };
    }

    return {
      connected: true,
      provider: "mcp_kv",
      mode: "mcp_kv",
      message: "Wordstat MCP-KV endpoint настроен на платформе."
    };
  }

  async collect(request: WordstatCollectRequest): Promise<WordstatCollectResult> {
    if (!this.endpoint) {
      throw new WordstatProviderUnavailableError("WORDSTAT_MCP_ENDPOINT не настроен.");
    }

    const payload = await postJson(this.endpoint, {
      projectId: request.projectId,
      semanticRunId: request.semanticRunId,
      region: request.region,
      phrases: request.seeds.map((seed) => seed.phrase),
      seeds: request.seeds
    });

    return normalizeProviderResponse("mcp_kv", "mcp_kv", request.region, request.seeds, payload);
  }
}

class YandexApiWordstatProvider implements WordstatProvider {
  constructor(private readonly config: Extract<WordstatRuntimeConfig, { provider: "yandex_api" }> | null) {}

  getStatus(): WordstatProviderStatus {
    if (!this.config) {
      return {
        connected: false,
        provider: "yandex_api",
        mode: "yandex_search_api",
        message: "Yandex Wordstat Search API не настроен: нужен folderId и API key или IAM token."
      };
    }

    return {
      connected: true,
      provider: "yandex_api",
      mode: "yandex_search_api",
      message: "Yandex Wordstat Search API настроен на платформе."
    };
  }

  async collect(request: WordstatCollectRequest): Promise<WordstatCollectResult> {
    if (!this.config) {
      throw new WordstatProviderUnavailableError("Yandex Wordstat Search API не настроен.");
    }

    const endpoint = `${this.config.apiBaseUrl.replace(/\/+$/, "")}/topRequests`;
    const authorization =
      this.config.authType === "iam_token" ? `Bearer ${this.config.apiToken}` : `Api-Key ${this.config.apiToken}`;
    const seedResponses = [];

    for (const seed of request.seeds) {
      const payload = await postJson(
        endpoint,
        {
          devices: this.config.devices,
          folderId: this.config.folderId,
          numPhrases: this.config.numPhrases,
          phrase: seed.phrase,
          regions: this.config.regionIds
        },
        {
          Authorization: authorization
        }
      );
      const record = asRecord(payload);
      const results = asArray(record.results ?? record.topRequests ?? record.items);
      const associations = asArray(record.associations ?? record.relatedQueries ?? record.related_queries);
      const exactMatch = results
        .map((item) => normalizeRelatedQuery(item))
        .find((item) => item?.phrase.toLowerCase() === seed.phrase.toLowerCase());

      seedResponses.push({
        phrase: seed.phrase,
        frequency: exactMatch?.frequency ?? null,
        relatedQueries: [...results, ...associations],
        raw: payload
      });
    }

    return normalizeProviderResponse(
      "yandex_api",
      "yandex_search_api",
      this.config.regionIds.join(","),
      request.seeds,
      {
        results: seedResponses
      }
    );
  }
}

export async function createWordstatProvider(): Promise<WordstatProvider> {
  const config = await getWordstatRuntimeConfig();

  if (config.provider === "mcp_kv") {
    return new McpKvWordstatProvider(config.mcpEndpoint);
  }

  if (config.provider === "yandex_api") {
    return new YandexApiWordstatProvider(config);
  }

  return new DisabledWordstatProvider();
}
