// ============================================
// GOOGLE ADS API CLIENT (REST v21)
// ============================================

import { GoogleAdsAuth } from "./auth.js";
import type { GoogleAdsClient, GoogleAdsConfig, MutateOperation } from "./types.js";

const API_VERSION = "v21";
const BASE_URL = `https://googleads.googleapis.com/${API_VERSION}`;

export class GoogleAdsRestClient implements GoogleAdsClient {
  private auth: GoogleAdsAuth;
  private defaultCustomerId: string;

  constructor(auth: GoogleAdsAuth, defaultCustomerId?: string) {
    this.auth = auth;
    this.defaultCustomerId = (defaultCustomerId || "").replace(/-/g, "");
  }

  getCustomerId(): string {
    return this.defaultCustomerId;
  }

  private async request(url: string, options: RequestInit = {}): Promise<any> {
    const token = await this.auth.getAccessToken();
    const headers = {
      ...this.auth.getHeaders(),
      Authorization: `Bearer ${token}`,
      ...options.headers,
    };

    const response = await fetch(url, { ...options, headers });

    if (!response.ok) {
      const errorBody = await response.text();
      let errorMessage = `Google Ads API error: ${response.status}`;
      try {
        const errorJson = JSON.parse(errorBody);
        const details = errorJson.error?.details?.[0]?.errors?.[0];
        if (details) {
          errorMessage += ` — ${details.errorCode ? JSON.stringify(details.errorCode) : ""} ${details.message || ""}`;
        }
      } catch {
        errorMessage += ` — ${errorBody.slice(0, 500)}`;
      }
      throw new Error(errorMessage);
    }

    return response.json();
  }

  /**
   * Execute a GAQL (Google Ads Query Language) query
   */
  async query(customerId: string, gaqlQuery: string): Promise<any[]> {
    const cid = (customerId || this.defaultCustomerId).replace(/-/g, "");
    if (!cid) throw new Error("Customer ID required. Pass it as a parameter or set GOOGLE_ADS_CUSTOMER_ID env var.");

    const url = `${BASE_URL}/customers/${cid}/googleAds:searchStream`;
    const data = await this.request(url, {
      method: "POST",
      body: JSON.stringify({ query: gaqlQuery }),
    });

    // searchStream returns array of result batches
    const results: any[] = [];
    if (Array.isArray(data)) {
      for (const batch of data) {
        if (batch.results) {
          results.push(...batch.results);
        }
      }
    } else if (data.results) {
      results.push(...data.results);
    }

    return results;
  }

  /**
   * Execute mutate operations (create, update, remove)
   */
  async mutate(customerId: string, operations: MutateOperation[]): Promise<any> {
    const cid = (customerId || this.defaultCustomerId).replace(/-/g, "");
    if (!cid) throw new Error("Customer ID required.");

    const mutateOps = operations.map((op) => {
      const key = `${op.entity}Operation`;
      const opBody: any = {};

      if (op.operation === "create") {
        opBody.create = op.resource;
      } else if (op.operation === "update") {
        opBody.update = op.resource;
        if (op.updateMask) {
          opBody.updateMask = op.updateMask;
        }
      } else if (op.operation === "remove") {
        opBody.remove = op.resourceName;
      }

      return { [key]: opBody };
    });

    const url = `${BASE_URL}/customers/${cid}/googleAds:mutate`;
    return this.request(url, {
      method: "POST",
      body: JSON.stringify({ mutateOperations: mutateOps }),
    });
  }

  /**
   * List all customer accounts accessible with current credentials
   */
  async listAccessibleCustomers(): Promise<string[]> {
    const url = `${BASE_URL}/customers:listAccessibleCustomers`;
    const data = await this.request(url, { method: "GET" });
    return (data.resourceNames || []).map((rn: string) => rn.replace("customers/", ""));
  }

  /**
   * Get a specific resource by resource name
   */
  async getResource(resourceName: string): Promise<any> {
    const url = `${BASE_URL}/${resourceName}`;
    return this.request(url, { method: "GET" });
  }

  /**
   * Generate keyword ideas using KeywordPlanIdeaService
   */
  async generateKeywordIdeas(
    customerId: string,
    options: {
      seedKeywords?: string[];
      seedUrl?: string;
      language?: string;
      geoTargets?: string[];
    }
  ): Promise<any[]> {
    const cid = (customerId || this.defaultCustomerId).replace(/-/g, "");
    const url = `${BASE_URL}/customers/${cid}:generateKeywordIdeas`;

    const body: any = {
      language: options.language || "languageConstants/1000", // English
      geoTargetConstants: options.geoTargets || ["geoTargetConstants/2840"], // US
      keywordPlanNetwork: "GOOGLE_SEARCH",
    };

    if (options.seedKeywords?.length) {
      body.keywordSeed = { keywords: options.seedKeywords };
    }
    if (options.seedUrl) {
      body.urlSeed = { url: options.seedUrl };
    }

    const data = await this.request(url, {
      method: "POST",
      body: JSON.stringify(body),
    });

    return data.results || [];
  }

  /**
   * Get recommendations for the account
   */
  async getRecommendations(customerId: string, types?: string[]): Promise<any[]> {
    const cid = (customerId || this.defaultCustomerId).replace(/-/g, "");
    let query = `SELECT recommendation.type, recommendation.impact, recommendation.campaign_budget_recommendation, recommendation.keyword_recommendation, recommendation.resource_name FROM recommendation`;
    if (types?.length) {
      query += ` WHERE recommendation.type IN (${types.map((t) => `'${t}'`).join(", ")})`;
    }
    query += ` LIMIT 50`;
    return this.query(cid, query);
  }

  static fromEnv(): GoogleAdsRestClient {
    const auth = GoogleAdsAuth.fromEnv();
    return new GoogleAdsRestClient(auth, process.env.GOOGLE_ADS_CUSTOMER_ID);
  }
}
