import { Client } from 'typesense';

export interface SearchServiceConfig {
  host: string;
  port: number;
  protocol: 'http' | 'https';
  apiKey: string;
}

export class SearchService {
  private client: Client;
  private ready = false;

  constructor(config: SearchServiceConfig) {
    this.client = new Client({
      nodes: [{
        host: config.host,
        port: config.port,
        protocol: config.protocol,
      }],
      apiKey: config.apiKey,
    });
  }

  get isReady(): boolean {
    return this.ready;
  }

  async search(collection: string, query: string, options: any = {}) {
    const searchParameters = {
      'q': query,
      'query_by': options.query_by || 'title,artist',
      ...options,
    };

    return await this.client.collections(collection).documents().search(searchParameters);
  }

  /**
   * Create the 'tracks' collection schema if it does not already exist.
   * Gracefully handles Typesense not being ready yet (503) — the collection
   * can be created later by calling ensureCollection again or via the admin
   * reindex endpoint. Search falls back to Postgres ILIKE when Typesense is
   * unavailable, so a missing collection is never a hard failure.
   */
  async ensureCollection(): Promise<void> {
    const collectionSchema = {
      name: 'tracks',
      fields: [
        { name: 'id', type: 'string' as const },
        { name: 'title', type: 'string' as const },
        { name: 'artist', type: 'string' as const },
        { name: 'album', type: 'string' as const },
        { name: 'duration', type: 'int32' as const },
        { name: 'play_count', type: 'int32' as const },
        { name: 'genre', type: 'string[]' as const, facet: true },
        { name: 'source_type', type: 'string' as const },
      ],
    };

    // First check if the collection already exists (retrieve succeeds).
    try {
      await this.client.collections(collectionSchema.name).retrieve();
      this.ready = true;
      return;
    } catch (checkErr: any) {
      // 404 means collection doesn't exist — proceed to create.
      // 503 means Typesense isn't ready yet — skip creation, search falls back.
      if (checkErr?.httpStatus === 503) {
        console.warn('[SearchService] Typesense not ready yet (503). Search will use Postgres ILIKE fallback.');
        return;
      }
    }

    // Collection doesn't exist — create it.
    try {
      await this.client.collections().create(collectionSchema);
      this.ready = true;
    } catch (createErr: any) {
      // 409 / "already exists" — another process created it between our check and create.
      if (createErr?.message?.includes('already exists')) {
        this.ready = true;
        return;
      }
      // 503 — Typesense not ready yet, not a hard failure.
      if (createErr?.httpStatus === 503) {
        console.warn('[SearchService] Typesense not ready yet (503). Search will use Postgres ILIKE fallback.');
        return;
      }
      // Unexpected error — log and continue; search falls back to Postgres.
      console.warn('[SearchService] Failed to create tracks collection:', createErr?.message ?? createErr);
    }
  }
}
