/**
 * Google Search Console API client wrapper
 * Provides typed interface to GSC API endpoints
 */

import { google, webmasters_v3, indexing_v3 } from 'googleapis';
import type { OAuth2Client } from 'google-auth-library';

export interface Site {
  siteUrl: string;
  permissionLevel: string;
}

export interface SiteDetails {
  siteUrl: string;
  permissionLevel: string;
  verified: boolean;
}

export interface SearchAnalyticsParams {
  startDate: string;
  endDate: string;
  dimensions?: string[];
  type?: 'web' | 'image' | 'video' | 'news' | 'discover' | 'googleNews';
  dimensionFilterGroups?: any[];
  aggregationType?: 'auto' | 'byProperty' | 'byPage';
  rowLimit?: number;
  startRow?: number;
  dataState?: 'all' | 'final';
}

export interface SearchAnalyticsResponse {
  rows?: Array<{
    keys?: string[];
    clicks: number;
    impressions: number;
    ctr: number;
    position: number;
  }>;
  responseAggregationType?: string;
}

export interface InspectionResult {
  inspectionResult: {
    indexStatusResult?: {
      verdict?: string;
      coverageState?: string;
      robotsTxtState?: string;
      indexingState?: string;
      lastCrawlTime?: string;
      pageFetchState?: string;
      googleCanonical?: string;
      userCanonical?: string;
    };
    mobileUsabilityResult?: {
      verdict?: string;
      issues?: Array<{ issueType: string; message: string }>;
    };
    richResultsResult?: {
      verdict?: string;
      detectedItems?: Array<{ richResultType: string }>;
    };
  };
}

export interface Sitemap {
  path: string;
  lastSubmitted?: string;
  isPending?: boolean;
  isSitemapsIndex?: boolean;
  type?: string;
  lastDownloaded?: string;
  warnings?: string;
  errors?: string;
  contents?: Array<{
    type: string;
    submitted: string;
    indexed: string;
  }>;
}

/**
 * Custom error types for better error handling
 */
export class GSCError extends Error {
  constructor(
    message: string,
    public code?: string,
    public statusCode?: number
  ) {
    super(message);
    this.name = 'GSCError';
  }
}

export class GSCClient {
  private webmasters: webmasters_v3.Webmasters;
  private indexing: indexing_v3.Indexing;
  private auth: OAuth2Client;

  constructor(authClient: OAuth2Client) {
    this.auth = authClient;
    this.webmasters = google.webmasters({ version: 'v3', auth: authClient });
    this.indexing = google.indexing({ version: 'v3', auth: authClient });
  }

  /**
   * List all properties user has access to
   */
  async listSites(): Promise<Site[]> {
    try {
      const response = await this.webmasters.sites.list();
      const sites = response.data.siteEntry || [];
      return sites.map(site => ({
        siteUrl: site.siteUrl || '',
        permissionLevel: site.permissionLevel || 'unknown'
      }));
    } catch (error: any) {
      throw new GSCError(
        `Failed to list sites: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Get details for a specific property
   */
  async getSiteDetails(siteUrl: string): Promise<SiteDetails> {
    try {
      const response = await this.webmasters.sites.get({ siteUrl });
      return {
        siteUrl: response.data.siteUrl || siteUrl,
        permissionLevel: response.data.permissionLevel || 'unknown',
        verified: true
      };
    } catch (error: any) {
      throw new GSCError(
        `Failed to get site details: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Add a new property to GSC
   */
  async addSite(siteUrl: string): Promise<void> {
    try {
      await this.webmasters.sites.add({ siteUrl });
    } catch (error: any) {
      throw new GSCError(
        `Failed to add site: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Remove a property from GSC
   */
  async removeSite(siteUrl: string): Promise<void> {
    try {
      await this.webmasters.sites.delete({ siteUrl });
    } catch (error: any) {
      throw new GSCError(
        `Failed to remove site: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Query Search Analytics data
   */
  async searchAnalytics(
    siteUrl: string,
    params: SearchAnalyticsParams
  ): Promise<SearchAnalyticsResponse> {
    try {
      const requestBody: any = {
        startDate: params.startDate,
        endDate: params.endDate,
        dimensions: params.dimensions,
        dimensionFilterGroups: params.dimensionFilterGroups,
        aggregationType: params.aggregationType,
        rowLimit: params.rowLimit || 1000,
        startRow: params.startRow || 0,
        dataState: params.dataState || 'final'
      };
      
      // Add type if specified (not in official types but works)
      if (params.type) {
        requestBody.type = params.type;
      }

      const response = await this.webmasters.searchanalytics.query({
        siteUrl,
        requestBody
      });

      return response.data as SearchAnalyticsResponse;
    } catch (error: any) {
      throw new GSCError(
        `Failed to query search analytics: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Inspect a URL using URL Inspection API
   * Note: This API requires special access and may not be available to all accounts
   */
  async inspectUrl(siteUrl: string, inspectionUrl: string): Promise<InspectionResult> {
    try {
      // URL Inspection API is not in public googleapis types
      // We need to use the REST API directly
      const urlInspection = (this.webmasters as any).urlInspection;
      
      if (!urlInspection) {
        throw new Error('URL Inspection API not available');
      }

      const response = await urlInspection.index.inspect({
        requestBody: {
          siteUrl,
          inspectionUrl
        }
      });

      return response.data as InspectionResult;
    } catch (error: any) {
      throw new GSCError(
        `Failed to inspect URL: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * List all sitemaps for a property
   */
  async listSitemaps(siteUrl: string): Promise<Sitemap[]> {
    try {
      const response = await this.webmasters.sitemaps.list({ siteUrl });
      const sitemaps = response.data.sitemap || [];
      return sitemaps.map(sm => ({
        path: sm.path || '',
        lastSubmitted: sm.lastSubmitted || undefined,
        isPending: sm.isPending || undefined,
        isSitemapsIndex: sm.isSitemapsIndex || undefined,
        type: sm.type || undefined,
        lastDownloaded: sm.lastDownloaded || undefined,
        warnings: sm.warnings ? String(sm.warnings) : undefined,
        errors: sm.errors ? String(sm.errors) : undefined,
        contents: sm.contents?.map(c => ({
          type: c.type || '',
          submitted: c.submitted || '0',
          indexed: c.indexed || '0'
        }))
      }));
    } catch (error: any) {
      throw new GSCError(
        `Failed to list sitemaps: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Get details for a specific sitemap
   */
  async getSitemap(siteUrl: string, feedpath: string): Promise<Sitemap> {
    try {
      const response = await this.webmasters.sitemaps.get({
        siteUrl,
        feedpath
      });
      const sm = response.data;
      return {
        path: sm.path || feedpath,
        lastSubmitted: sm.lastSubmitted || undefined,
        isPending: sm.isPending || undefined,
        isSitemapsIndex: sm.isSitemapsIndex || undefined,
        type: sm.type || undefined,
        lastDownloaded: sm.lastDownloaded || undefined,
        warnings: sm.warnings ? String(sm.warnings) : undefined,
        errors: sm.errors ? String(sm.errors) : undefined,
        contents: sm.contents?.map(c => ({
          type: c.type || '',
          submitted: c.submitted || '0',
          indexed: c.indexed || '0'
        }))
      };
    } catch (error: any) {
      throw new GSCError(
        `Failed to get sitemap: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Submit a sitemap to GSC
   */
  async submitSitemap(siteUrl: string, feedpath: string): Promise<void> {
    try {
      await this.webmasters.sitemaps.submit({
        siteUrl,
        feedpath
      });
    } catch (error: any) {
      throw new GSCError(
        `Failed to submit sitemap: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Delete a sitemap from GSC
   */
  async deleteSitemap(siteUrl: string, feedpath: string): Promise<void> {
    try {
      await this.webmasters.sitemaps.delete({
        siteUrl,
        feedpath
      });
    } catch (error: any) {
      throw new GSCError(
        `Failed to delete sitemap: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Request indexing for a URL using Indexing API
   */
  async requestIndexing(url: string, type: 'URL_UPDATED' | 'URL_DELETED' = 'URL_UPDATED'): Promise<void> {
    try {
      await this.indexing.urlNotifications.publish({
        requestBody: {
          url,
          type
        }
      });
    } catch (error: any) {
      throw new GSCError(
        `Failed to request indexing: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }

  /**
   * Get indexing status for a URL
   */
  async getIndexingStatus(url: string): Promise<any> {
    try {
      const response = await this.indexing.urlNotifications.getMetadata({ url });
      return response.data;
    } catch (error: any) {
      throw new GSCError(
        `Failed to get indexing status: ${error.message}`,
        error.code,
        error.statusCode
      );
    }
  }
}
