import * as plugins from '../plugins.js';
import type { IStorageObject } from '@design.estate/dees-catalog';
export type { IStorageObject };

export interface IMongoDatabase {
  name: string;
  sizeOnDisk?: number;
  empty?: boolean;
}

export interface IMongoCollection {
  name: string;
  count?: number;
}

export interface IMongoIndex {
  name: string;
  keys: Record<string, number>;
  unique?: boolean;
  sparse?: boolean;
}

export interface ICollectionStats {
  count: number;
  size: number;
  avgObjSize: number;
  storageSize: number;
  indexCount: number;
}

export interface IDatabaseStats {
  collections: number;
  views: number;
  objects: number;
  avgObjSize: number;
  dataSize: number;
  storageSize: number;
  indexes: number;
  indexSize: number;
}

/**
 * API service for communicating with the tsview backend
 */
export class ApiService {
  private baseUrl: string;

  constructor() {
    // Use current origin for API calls - TypedServer expects /typedrequest endpoint
    this.baseUrl = window.location.origin + '/typedrequest';
  }

  /**
   * Make a typed request to the backend
   */
  private async request<TReq, TRes>(method: string, requestData: TReq): Promise<TRes> {
    const typedRequest = new plugins.typedrequest.TypedRequest<{
      method: string;
      request: TReq;
      response: TRes;
    }>(this.baseUrl, method);

    const response = await typedRequest.fire(requestData);
    return response;
  }

  // ===========================================
  // S3 API Methods
  // ===========================================

  async listBuckets(): Promise<string[]> {
    const result = await this.request<{}, { buckets: string[] }>('listBuckets', {});
    return result.buckets;
  }

  async createBucket(bucketName: string): Promise<boolean> {
    const result = await this.request<{ bucketName: string }, { success: boolean }>(
      'createBucket',
      { bucketName }
    );
    return result.success;
  }

  async deleteBucket(bucketName: string): Promise<boolean> {
    const result = await this.request<{ bucketName: string }, { success: boolean }>(
      'deleteBucket',
      { bucketName }
    );
    return result.success;
  }

  async listObjects(
    bucketName: string,
    prefix?: string,
    delimiter?: string
  ): Promise<{ objects: IStorageObject[]; prefixes: string[] }> {
    return this.request('listObjects', { bucketName, prefix, delimiter });
  }

  async getObject(
    bucketName: string,
    key: string
  ): Promise<{ content: string; contentType: string; size: number; lastModified: string }> {
    return this.request('getObject', { bucketName, key });
  }

  async getObjectMetadata(
    bucketName: string,
    key: string
  ): Promise<{ contentType: string; size: number; lastModified: string }> {
    return this.request('getObjectMetadata', { bucketName, key });
  }

  async putObject(
    bucketName: string,
    key: string,
    content: string,
    contentType: string
  ): Promise<boolean> {
    const result = await this.request<
      { bucketName: string; key: string; content: string; contentType: string },
      { success: boolean }
    >('putObject', { bucketName, key, content, contentType });
    return result.success;
  }

  async deleteObject(bucketName: string, key: string): Promise<boolean> {
    const result = await this.request<
      { bucketName: string; key: string },
      { success: boolean }
    >('deleteObject', { bucketName, key });
    return result.success;
  }

  async deletePrefix(bucketName: string, prefix: string): Promise<boolean> {
    const result = await this.request<
      { bucketName: string; prefix: string },
      { success: boolean }
    >('deletePrefix', { bucketName, prefix });
    return result.success;
  }

  async getObjectUrl(bucketName: string, key: string): Promise<string> {
    const result = await this.request<
      { bucketName: string; key: string },
      { url: string }
    >('getObjectUrl', { bucketName, key });
    return result.url;
  }

  async copyObject(
    sourceBucket: string,
    sourceKey: string,
    destBucket: string,
    destKey: string
  ): Promise<boolean> {
    const result = await this.request<
      { sourceBucket: string; sourceKey: string; destBucket: string; destKey: string },
      { success: boolean }
    >('copyObject', { sourceBucket, sourceKey, destBucket, destKey });
    return result.success;
  }

  async moveObject(
    bucketName: string,
    sourceKey: string,
    destKey: string
  ): Promise<{ success: boolean; error?: string }> {
    return this.request<
      { bucketName: string; sourceKey: string; destKey: string },
      { success: boolean; error?: string }
    >('moveObject', { bucketName, sourceKey, destKey });
  }

  async movePrefix(
    bucketName: string,
    sourcePrefix: string,
    destPrefix: string
  ): Promise<{ success: boolean; movedCount: number; error?: string }> {
    return this.request<
      { bucketName: string; sourcePrefix: string; destPrefix: string },
      { success: boolean; movedCount: number; error?: string }
    >('movePrefix', { bucketName, sourcePrefix, destPrefix });
  }

  // ===========================================
  // MongoDB API Methods
  // ===========================================

  async listDatabases(): Promise<IMongoDatabase[]> {
    const result = await this.request<{}, { databases: IMongoDatabase[] }>(
      'listDatabases',
      {}
    );
    return result.databases;
  }

  async createDatabase(databaseName: string): Promise<boolean> {
    const result = await this.request<{ databaseName: string }, { success: boolean }>(
      'createDatabase',
      { databaseName }
    );
    return result.success;
  }

  async dropDatabase(databaseName: string): Promise<boolean> {
    const result = await this.request<{ databaseName: string }, { success: boolean }>(
      'dropDatabase',
      { databaseName }
    );
    return result.success;
  }

  async listCollections(databaseName: string): Promise<IMongoCollection[]> {
    const result = await this.request<
      { databaseName: string },
      { collections: IMongoCollection[] }
    >('listCollections', { databaseName });
    return result.collections;
  }

  async createCollection(databaseName: string, collectionName: string): Promise<boolean> {
    const result = await this.request<
      { databaseName: string; collectionName: string },
      { success: boolean }
    >('createCollection', { databaseName, collectionName });
    return result.success;
  }

  async dropCollection(databaseName: string, collectionName: string): Promise<boolean> {
    const result = await this.request<
      { databaseName: string; collectionName: string },
      { success: boolean }
    >('dropCollection', { databaseName, collectionName });
    return result.success;
  }

  async findDocuments(
    databaseName: string,
    collectionName: string,
    options?: {
      filter?: Record<string, unknown>;
      projection?: Record<string, unknown>;
      sort?: Record<string, number>;
      skip?: number;
      limit?: number;
    }
  ): Promise<{ documents: Record<string, unknown>[]; total: number }> {
    return this.request('findDocuments', {
      databaseName,
      collectionName,
      ...options,
    });
  }

  async getDocument(
    databaseName: string,
    collectionName: string,
    documentId: string
  ): Promise<Record<string, unknown> | null> {
    const result = await this.request<
      { databaseName: string; collectionName: string; documentId: string },
      { document: Record<string, unknown> | null }
    >('getDocument', { databaseName, collectionName, documentId });
    return result.document;
  }

  async insertDocument(
    databaseName: string,
    collectionName: string,
    document: Record<string, unknown>
  ): Promise<string> {
    const result = await this.request<
      { databaseName: string; collectionName: string; document: Record<string, unknown> },
      { insertedId: string }
    >('insertDocument', { databaseName, collectionName, document });
    return result.insertedId;
  }

  async updateDocument(
    databaseName: string,
    collectionName: string,
    documentId: string,
    update: Record<string, unknown>
  ): Promise<{ success: boolean; modifiedCount: number }> {
    return this.request('updateDocument', {
      databaseName,
      collectionName,
      documentId,
      update,
    });
  }

  async deleteDocument(
    databaseName: string,
    collectionName: string,
    documentId: string
  ): Promise<{ success: boolean; deletedCount: number }> {
    return this.request('deleteDocument', { databaseName, collectionName, documentId });
  }

  async runAggregation(
    databaseName: string,
    collectionName: string,
    pipeline: Record<string, unknown>[]
  ): Promise<Record<string, unknown>[]> {
    const result = await this.request<
      { databaseName: string; collectionName: string; pipeline: Record<string, unknown>[] },
      { results: Record<string, unknown>[] }
    >('runAggregation', { databaseName, collectionName, pipeline });
    return result.results;
  }

  async listIndexes(
    databaseName: string,
    collectionName: string
  ): Promise<IMongoIndex[]> {
    const result = await this.request<
      { databaseName: string; collectionName: string },
      { indexes: IMongoIndex[] }
    >('listIndexes', { databaseName, collectionName });
    return result.indexes;
  }

  async createIndex(
    databaseName: string,
    collectionName: string,
    keys: Record<string, number>,
    options?: { unique?: boolean; sparse?: boolean; name?: string }
  ): Promise<string> {
    const result = await this.request<
      {
        databaseName: string;
        collectionName: string;
        keys: Record<string, number>;
        options?: { unique?: boolean; sparse?: boolean; name?: string };
      },
      { indexName: string }
    >('createIndex', { databaseName, collectionName, keys, options });
    return result.indexName;
  }

  async dropIndex(
    databaseName: string,
    collectionName: string,
    indexName: string
  ): Promise<boolean> {
    const result = await this.request<
      { databaseName: string; collectionName: string; indexName: string },
      { success: boolean }
    >('dropIndex', { databaseName, collectionName, indexName });
    return result.success;
  }

  async getCollectionStats(
    databaseName: string,
    collectionName: string
  ): Promise<ICollectionStats> {
    const result = await this.request<
      { databaseName: string; collectionName: string },
      { stats: ICollectionStats }
    >('getCollectionStats', { databaseName, collectionName });
    return result.stats;
  }

  async getServerStatus(): Promise<{
    version: string;
    uptime: number;
    connections: { current: number; available: number };
  }> {
    return this.request('getServerStatus', {});
  }

  async getDatabaseStats(databaseName: string): Promise<IDatabaseStats | null> {
    const result = await this.request<
      { databaseName: string },
      { stats: IDatabaseStats | null }
    >('getDatabaseStats', { databaseName });
    return result.stats;
  }
}
