import { debug } from "../utils/logger";
import { get } from "node:https";
import { URL } from "node:url";
import { CacheManager } from "../utils/cache";

export interface ModelPricing {
  name: string;
  input: number;
  cache_write_5m: number;
  cache_write_1h: number;
  cache_read: number;
  output: number;
}
const OFFLINE_PRICING_DATA: Record<string, ModelPricing> = {
  "claude-haiku-4-5-20251001": {
    name: "Claude Haiku 4.5",
    input: 1.0,
    output: 5.0,
    cache_write_5m: 1.25,
    cache_write_1h: 2.0,
    cache_read: 0.1,
  },
  "claude-haiku-4-5": {
    name: "Claude Haiku 4.5",
    input: 1.0,
    output: 5.0,
    cache_write_5m: 1.25,
    cache_write_1h: 2.0,
    cache_read: 0.1,
  },
  "claude-opus-4-20250514": {
    name: "Claude Opus 4",
    input: 15.0,
    output: 75.0,
    cache_write_5m: 18.75,
    cache_write_1h: 30.0,
    cache_read: 1.5,
  },
  "claude-opus-4-1": {
    name: "Claude Opus 4.1",
    input: 15.0,
    output: 75.0,
    cache_write_5m: 18.75,
    cache_write_1h: 30.0,
    cache_read: 1.5,
  },
  "claude-opus-4-1-20250805": {
    name: "Claude Opus 4.1",
    input: 15.0,
    output: 75.0,
    cache_write_5m: 18.75,
    cache_write_1h: 30.0,
    cache_read: 1.5,
  },
  "claude-sonnet-4-20250514": {
    name: "Claude Sonnet 4",
    input: 3.0,
    output: 15.0,
    cache_write_5m: 3.75,
    cache_write_1h: 6.0,
    cache_read: 0.3,
  },
  "claude-4-opus-20250514": {
    name: "Claude 4 Opus",
    input: 15.0,
    output: 75.0,
    cache_write_5m: 18.75,
    cache_write_1h: 30.0,
    cache_read: 1.5,
  },
  "claude-4-sonnet-20250514": {
    name: "Claude 4 Sonnet",
    input: 3.0,
    output: 15.0,
    cache_write_5m: 3.75,
    cache_write_1h: 6.0,
    cache_read: 0.3,
  },
  "claude-sonnet-4-5": {
    name: "Claude Sonnet 4.5",
    input: 3.0,
    output: 15.0,
    cache_write_5m: 3.75,
    cache_write_1h: 6.0,
    cache_read: 0.3,
  },
  "claude-sonnet-4-5-20250929": {
    name: "Claude Sonnet 4.5",
    input: 3.0,
    output: 15.0,
    cache_write_5m: 3.75,
    cache_write_1h: 6.0,
    cache_read: 0.3,
  },
  "claude-opus-4-5": {
    name: "Claude Opus 4.5",
    input: 5.0,
    output: 25.0,
    cache_write_5m: 6.25,
    cache_write_1h: 10.0,
    cache_read: 0.5,
  },
  "claude-opus-4-5-20251101": {
    name: "Claude Opus 4.5",
    input: 5.0,
    output: 25.0,
    cache_write_5m: 6.25,
    cache_write_1h: 10.0,
    cache_read: 0.5,
  },
  "claude-opus-4-6": {
    name: "Claude Opus 4.6",
    input: 5.0,
    output: 25.0,
    cache_write_5m: 6.25,
    cache_write_1h: 10.0,
    cache_read: 0.5,
  },
  "claude-opus-4-6-20260205": {
    name: "Claude Opus 4.6",
    input: 5.0,
    output: 25.0,
    cache_write_5m: 6.25,
    cache_write_1h: 10.0,
    cache_read: 0.5,
  },
  "claude-sonnet-4-6": {
    name: "Claude Sonnet 4.6",
    input: 3.0,
    output: 15.0,
    cache_write_5m: 3.75,
    cache_write_1h: 6.0,
    cache_read: 0.3,
  },
};

export class PricingService {
  private static executionCache: Record<string, ModelPricing> | null = null;
  private static modelPricingCache = new Map<string, ModelPricing>();
  private static readonly GITHUB_PRICING_URL =
    "https://raw.githubusercontent.com/Owloops/claude-powerline/main/pricing.json";

  private static async loadDiskCache(): Promise<Record<
    string,
    ModelPricing
  > | null> {
    const TTL_24H = 24 * 60 * 60 * 1000;
    const minValidTime = Date.now() - TTL_24H;
    return (await CacheManager.getUsageCache(
      "pricing",
      minValidTime,
    )) as Record<string, ModelPricing> | null;
  }

  private static async saveDiskCache(
    data: Record<string, ModelPricing>,
  ): Promise<void> {
    await CacheManager.setUsageCache("pricing", data);
  }

  private static async fetchPricingData(): Promise<Record<
    string,
    ModelPricing
  > | null> {
    return new Promise((resolve) => {
      const parsedUrl = new URL(this.GITHUB_PRICING_URL);

      const request = get(
        {
          hostname: parsedUrl.hostname,
          path: parsedUrl.pathname,
          headers: {
            "User-Agent": "claude-powerline",
            "Cache-Control": "no-cache",
          },
          timeout: 5000,
        },
        (response) => {
          if (response.statusCode !== 200) {
            debug(`HTTP ${response.statusCode}: ${response.statusMessage}`);
            resolve(null);
            return;
          }

          let data = "";
          let size = 0;
          const MAX_SIZE = 1024 * 1024;

          response.on("data", (chunk) => {
            size += chunk.length;
            if (size > MAX_SIZE) {
              debug("Response too large");
              request.destroy();
              resolve(null);
              return;
            }
            data += chunk;
          });

          response.on("end", () => {
            try {
              const json = JSON.parse(data);
              const dataObj = json as Record<string, unknown>;
              const meta = dataObj._meta as { updated?: string } | undefined;

              const pricingData: Record<string, unknown> = {};
              for (const [key, value] of Object.entries(dataObj)) {
                if (key !== "_meta") {
                  pricingData[key] = value;
                }
              }

              if (this.validatePricingData(pricingData)) {
                debug(
                  `Fetched fresh pricing from GitHub for ${Object.keys(pricingData).length} models`,
                );
                debug(`Pricing last updated: ${meta?.updated || "unknown"}`);
                resolve(pricingData);
              } else {
                debug("Invalid pricing data structure");
                resolve(null);
              }
            } catch (error) {
              debug("Failed to parse JSON:", error);
              resolve(null);
            }
          });

          response.on("error", (error) => {
            debug("Response error:", error);
            resolve(null);
          });
        },
      );

      request.on("error", (error) => {
        debug("Request error:", error);
        resolve(null);
      });

      request.on("timeout", () => {
        debug("Request timeout");
        request.destroy();
        resolve(null);
      });

      request.end();
    });
  }

  static async getCurrentPricing(): Promise<Record<string, ModelPricing>> {
    if (this.executionCache !== null) {
      debug(
        `[CACHE-HIT] Pricing execution cache: ${Object.keys(this.executionCache).length} models`,
      );
      return this.executionCache;
    }

    const diskCached = await this.loadDiskCache();
    if (diskCached) {
      debug(
        `[CACHE-HIT] Pricing disk cache: ${Object.keys(diskCached).length} models`,
      );
      this.executionCache = diskCached;
      debug(
        `[CACHE-SET] Pricing execution cache stored: ${Object.keys(diskCached).length} models`,
      );
      return diskCached;
    }

    const freshData = await this.fetchPricingData();
    if (freshData) {
      await this.saveDiskCache(freshData);
      debug(
        `[CACHE-SET] Pricing disk cache stored: ${Object.keys(freshData).length} models`,
      );
      this.executionCache = freshData;
      debug(
        `[CACHE-SET] Pricing execution cache stored: ${Object.keys(freshData).length} models`,
      );
      return freshData;
    }

    debug(
      `[CACHE-FALLBACK] Using offline pricing data: ${Object.keys(OFFLINE_PRICING_DATA).length} models`,
    );
    this.executionCache = OFFLINE_PRICING_DATA;
    debug(
      `[CACHE-SET] Pricing execution cache stored: ${Object.keys(OFFLINE_PRICING_DATA).length} models`,
    );
    return OFFLINE_PRICING_DATA;
  }

  private static validatePricingData(
    data: unknown,
  ): data is Record<string, ModelPricing> {
    if (!data || typeof data !== "object") return false;

    for (const [, value] of Object.entries(data)) {
      if (!value || typeof value !== "object") return false;
      const pricing = value as Record<string, unknown>;

      if (
        typeof pricing.input !== "number" ||
        typeof pricing.output !== "number" ||
        typeof pricing.cache_read !== "number"
      ) {
        return false;
      }
    }

    return true;
  }

  static async getModelPricing(modelId: string): Promise<ModelPricing> {
    if (this.modelPricingCache.has(modelId)) {
      debug(`[CACHE-HIT] Model pricing cache: ${modelId}`);
      return this.modelPricingCache.get(modelId)!;
    }

    const allPricing = await this.getCurrentPricing();
    let pricing: ModelPricing;

    if (allPricing[modelId]) {
      pricing = allPricing[modelId];
    } else {
      pricing = this.fuzzyMatchModel(modelId, allPricing);
    }

    this.modelPricingCache.set(modelId, pricing);
    debug(`[CACHE-SET] Model pricing cache: ${modelId}`);
    return pricing;
  }

  private static fuzzyMatchModel(
    modelId: string,
    allPricing: Record<string, ModelPricing>,
  ): ModelPricing {
    const lowerModelId = modelId.toLowerCase();

    for (const [key, pricing] of Object.entries(allPricing)) {
      if (key.toLowerCase() === lowerModelId) {
        return pricing;
      }
    }
    const patterns = [
      {
        pattern: ["opus-4-6", "claude-opus-4-6"],
        fallback: "claude-opus-4-6-20260205",
      },
      {
        pattern: ["opus-4-5", "claude-opus-4-5"],
        fallback: "claude-opus-4-5-20251101",
      },
      {
        pattern: ["opus-4-1", "claude-opus-4-1"],
        fallback: "claude-opus-4-1-20250805",
      },
      {
        pattern: ["opus-4", "claude-opus-4"],
        fallback: "claude-opus-4-20250514",
      },
      {
        pattern: ["sonnet-4-6", "sonnet-4.6", "claude-sonnet-4-6"],
        fallback: "claude-sonnet-4-6",
      },
      {
        pattern: ["sonnet-4.5", "4-5-sonnet", "sonnet-4-5"],
        fallback: "claude-sonnet-4-5-20250929",
      },
      {
        pattern: ["sonnet-4", "claude-sonnet-4"],
        fallback: "claude-sonnet-4-20250514",
      },
      {
        pattern: ["haiku-4.5", "4-5-haiku", "haiku-4-5"],
        fallback: "claude-haiku-4-5-20251001",
      },
      { pattern: ["haiku"], fallback: "claude-haiku-4-5-20251001" },
      { pattern: ["opus"], fallback: "claude-opus-4-20250514" },
      { pattern: ["sonnet"], fallback: "claude-sonnet-4-5-20250929" },
    ];

    for (const { pattern, fallback } of patterns) {
      if (pattern.some((p) => lowerModelId.includes(p))) {
        if (allPricing[fallback]) {
          return allPricing[fallback];
        }
      }
    }

    return (
      allPricing["claude-sonnet-4-5-20250929"] || {
        name: `${modelId} (Unknown Model)`,
        input: 3.0,
        cache_write_5m: 3.75,
        cache_write_1h: 6.0,
        cache_read: 0.3,
        output: 15.0,
      }
    );
  }

  static async calculateCostForEntry(
    entry: Record<string, unknown>,
  ): Promise<number> {
    const message = entry.message as Record<string, unknown> | undefined;
    const usage = message?.usage as Record<string, number> | undefined;
    if (!usage) {
      return 0;
    }

    const modelId = this.extractModelId(entry);
    const pricing = await this.getModelPricing(modelId);

    const inputTokens = usage.input_tokens || 0;
    const outputTokens = usage.output_tokens || 0;
    const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
    const cacheReadTokens = usage.cache_read_input_tokens || 0;

    const inputCost = (inputTokens / 1_000_000) * pricing.input;
    const outputCost = (outputTokens / 1_000_000) * pricing.output;
    const cacheReadCost = (cacheReadTokens / 1_000_000) * pricing.cache_read;
    const cacheCreationCost =
      (cacheCreationTokens / 1_000_000) * pricing.cache_write_5m;

    return inputCost + outputCost + cacheCreationCost + cacheReadCost;
  }

  private static extractModelId(entry: Record<string, unknown>): string {
    if (entry.model && typeof entry.model === "string") {
      return entry.model;
    }

    const message = entry.message as Record<string, unknown> | undefined;
    if (message?.model) {
      const model = message.model;
      if (typeof model === "string") {
        return model;
      }
      const modelObj = model as Record<string, unknown> | undefined;
      return (
        (typeof modelObj?.id === "string" ? modelObj.id : null) ||
        "claude-sonnet-4-5-20250929"
      );
    }

    if (entry.model_id && typeof entry.model_id === "string") {
      return entry.model_id;
    }

    return "claude-sonnet-4-5-20250929";
  }
}
