import { Pool } from 'pg';
import { getDatabasePool } from '../client';
import bcrypt from 'bcrypt';

export interface ApiKey {
  id?: number;
  provider: 'coingecko' | 'coinmarketcap' | 'dexscreener' | 'custom';
  keyName: string;
  apiKeyEncrypted: string;
  isActive: boolean;
  rateLimitPerMinute?: number;
  rateLimitPerDay?: number;
  lastUsedAt?: Date;
  expiresAt?: Date;
  createdBy?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

export interface ApiEndpoint {
  id?: number;
  chainId: number;
  endpointType: 'rpc' | 'explorer' | 'indexer' | 'custom';
  endpointName: string;
  endpointUrl: string;
  isPrimary: boolean;
  isActive: boolean;
  requiresAuth: boolean;
  authType?: 'jwt' | 'api_key' | 'basic' | 'none';
  authConfig?: Record<string, unknown>;
  rateLimitPerMinute?: number;
  timeoutMs: number;
  healthCheckEnabled: boolean;
  lastHealthCheck?: Date;
  healthCheckStatus?: 'healthy' | 'unhealthy' | 'unknown';
  createdBy?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

export interface DexFactoryConfig {
  id?: number;
  chainId: number;
  dexType: 'uniswap_v2' | 'uniswap_v3' | 'sushiswap' | 'dodo' | 'custom';
  factoryAddress: string;
  routerAddress?: string;
  poolManagerAddress?: string;
  startBlock: number;
  isActive: boolean;
  description?: string;
  createdBy?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

export interface AdminUser {
  id?: number;
  username: string;
  email?: string;
  passwordHash: string;
  role: 'super_admin' | 'admin' | 'operator' | 'viewer';
  isActive: boolean;
  lastLogin?: Date;
  createdAt?: Date;
  updatedAt?: Date;
}

export class AdminRepository {
  public pool: Pool;

  constructor() {
    this.pool = getDatabasePool();
  }

  // API Keys Management
  async createApiKey(apiKey: ApiKey): Promise<ApiKey> {
    const result = await this.pool.query(
      `INSERT INTO api_keys (
         provider, key_name, api_key_encrypted, is_active,
         rate_limit_per_minute, rate_limit_per_day, expires_at, created_by
       )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
       RETURNING *`,
      [
        apiKey.provider,
        apiKey.keyName,
        apiKey.apiKeyEncrypted,
        apiKey.isActive,
        apiKey.rateLimitPerMinute,
        apiKey.rateLimitPerDay,
        apiKey.expiresAt,
        apiKey.createdBy,
      ]
    );

    return this.mapApiKey(result.rows[0]);
  }

  async getApiKeys(provider?: string): Promise<ApiKey[]> {
    let query = `SELECT * FROM api_keys WHERE is_active = true`;
    const params: (string | number)[] = [];

    if (provider) {
      query += ` AND provider = $1`;
      params.push(provider);
    }

    query += ` ORDER BY created_at DESC`;

    const result = await this.pool.query(query, params);
    return result.rows.map((row) => this.mapApiKey(row));
  }

  async getApiKey(id: number): Promise<ApiKey | null> {
    const result = await this.pool.query(`SELECT * FROM api_keys WHERE id = $1`, [id]);
    if (result.rows.length === 0) return null;
    return this.mapApiKey(result.rows[0]);
  }

  async updateApiKey(id: number, updates: Partial<ApiKey>): Promise<void> {
    const fields: string[] = [];
    const values: (string | number | boolean | Date | null)[] = [];
    let paramCount = 1;

    if (updates.isActive !== undefined) {
      fields.push(`is_active = $${paramCount++}`);
      values.push(updates.isActive);
    }
    if (updates.rateLimitPerMinute !== undefined) {
      fields.push(`rate_limit_per_minute = $${paramCount++}`);
      values.push(updates.rateLimitPerMinute);
    }
    if (updates.expiresAt !== undefined) {
      fields.push(`expires_at = $${paramCount++}`);
      values.push(updates.expiresAt);
    }

    if (fields.length === 0) return;

    values.push(id);
    await this.pool.query(
      `UPDATE api_keys SET ${fields.join(', ')}, updated_at = NOW() WHERE id = $${paramCount}`,
      values
    );
  }

  async deleteApiKey(id: number): Promise<void> {
    await this.pool.query(`UPDATE api_keys SET is_active = false, updated_at = NOW() WHERE id = $1`, [id]);
  }

  // API Endpoints Management
  async createEndpoint(endpoint: ApiEndpoint): Promise<ApiEndpoint> {
    const result = await this.pool.query(
      `INSERT INTO api_endpoints (
         chain_id, endpoint_type, endpoint_name, endpoint_url,
         is_primary, is_active, requires_auth, auth_type, auth_config,
         rate_limit_per_minute, timeout_ms, health_check_enabled, created_by
       )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
       RETURNING *`,
      [
        endpoint.chainId,
        endpoint.endpointType,
        endpoint.endpointName,
        endpoint.endpointUrl,
        endpoint.isPrimary,
        endpoint.isActive,
        endpoint.requiresAuth,
        endpoint.authType,
        endpoint.authConfig ? JSON.stringify(endpoint.authConfig) : null,
        endpoint.rateLimitPerMinute,
        endpoint.timeoutMs,
        endpoint.healthCheckEnabled,
        endpoint.createdBy,
      ]
    );

    return this.mapEndpoint(result.rows[0]);
  }

  async getEndpoints(chainId?: number, endpointType?: string): Promise<ApiEndpoint[]> {
    let query = `SELECT * FROM api_endpoints WHERE is_active = true`;
    const params: (string | number)[] = [];
    let paramCount = 1;

    if (chainId) {
      query += ` AND chain_id = $${paramCount++}`;
      params.push(chainId);
    }
    if (endpointType) {
      query += ` AND endpoint_type = $${paramCount++}`;
      params.push(endpointType);
    }

    query += ` ORDER BY chain_id, endpoint_type, is_primary DESC`;

    const result = await this.pool.query(query, params);
    return result.rows.map((row) => this.mapEndpoint(row));
  }

  async updateEndpoint(id: number, updates: Partial<ApiEndpoint>): Promise<void> {
    const fields: string[] = [];
    const values: (string | number | boolean | Date | null)[] = [];
    let paramCount = 1;

    if (updates.endpointUrl !== undefined) {
      fields.push(`endpoint_url = $${paramCount++}`);
      values.push(updates.endpointUrl);
    }
    if (updates.isActive !== undefined) {
      fields.push(`is_active = $${paramCount++}`);
      values.push(updates.isActive);
    }
    if (updates.isPrimary !== undefined) {
      fields.push(`is_primary = $${paramCount++}`);
      values.push(updates.isPrimary);
    }
    if (updates.healthCheckStatus !== undefined) {
      fields.push(`health_check_status = $${paramCount++}, last_health_check = NOW()`);
      values.push(updates.healthCheckStatus);
    }

    if (fields.length === 0) return;

    values.push(id);
    await this.pool.query(
      `UPDATE api_endpoints SET ${fields.join(', ')}, updated_at = NOW() WHERE id = $${paramCount}`,
      values
    );
  }

  // DEX Factory Management
  async createDexFactory(config: DexFactoryConfig): Promise<DexFactoryConfig> {
    const result = await this.pool.query(
      `INSERT INTO dex_factory_config (
         chain_id, dex_type, factory_address, router_address,
         pool_manager_address, start_block, is_active, description, created_by
       )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
       RETURNING *`,
      [
        config.chainId,
        config.dexType,
        config.factoryAddress.toLowerCase(),
        config.routerAddress?.toLowerCase(),
        config.poolManagerAddress?.toLowerCase(),
        config.startBlock,
        config.isActive,
        config.description,
        config.createdBy,
      ]
    );

    return this.mapDexFactory(result.rows[0]);
  }

  async getDexFactories(chainId?: number): Promise<DexFactoryConfig[]> {
    let query = `SELECT * FROM dex_factory_config WHERE is_active = true`;
    const params: (string | number)[] = [];

    if (chainId) {
      query += ` AND chain_id = $1`;
      params.push(chainId);
    }

    query += ` ORDER BY chain_id, dex_type`;

    const result = await this.pool.query(query, params);
    return result.rows.map((row) => this.mapDexFactory(row));
  }

  // Admin Users
  async createAdminUser(user: Omit<AdminUser, 'id' | 'createdAt' | 'updatedAt'>): Promise<AdminUser> {
    const result = await this.pool.query(
      `INSERT INTO admin_users (username, email, password_hash, role, is_active)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING *`,
      [user.username, user.email, user.passwordHash, user.role, user.isActive]
    );

    return this.mapAdminUser(result.rows[0]);
  }

  async getAdminUserByUsername(username: string): Promise<AdminUser | null> {
    const result = await this.pool.query(`SELECT * FROM admin_users WHERE username = $1`, [username]);
    if (result.rows.length === 0) return null;
    return this.mapAdminUser(result.rows[0]);
  }

  async verifyPassword(user: AdminUser, password: string): Promise<boolean> {
    return bcrypt.compare(password, user.passwordHash);
  }

  async hashPassword(password: string): Promise<string> {
    return bcrypt.hash(password, 10);
  }

  // Audit Log
  async createAuditLog(
    userId: number | null,
    action: string,
    resourceType: string,
    resourceId: number | null,
    oldValues: Record<string, unknown> | null,
    newValues: Record<string, unknown> | null,
    ipAddress?: string,
    userAgent?: string
  ): Promise<void> {
    await this.pool.query(
      `INSERT INTO admin_audit_log (
         user_id, action, resource_type, resource_id,
         old_values, new_values, ip_address, user_agent
       )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
      [
        userId,
        action,
        resourceType,
        resourceId,
        oldValues ? JSON.stringify(oldValues) : null,
        newValues ? JSON.stringify(newValues) : null,
        ipAddress,
        userAgent,
      ]
    );
  }

  // Mappers (row from pg has dynamic keys; use type assertion for type safety)
  /* eslint-disable @typescript-eslint/no-explicit-any */
  private mapApiKey(row: any): ApiKey {
    return {
      id: row.id,
      provider: row.provider,
      keyName: row.key_name,
      apiKeyEncrypted: row.api_key_encrypted,
      isActive: row.is_active,
      rateLimitPerMinute: row.rate_limit_per_minute,
      rateLimitPerDay: row.rate_limit_per_day,
      lastUsedAt: row.last_used_at,
      expiresAt: row.expires_at,
      createdBy: row.created_by,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  private mapEndpoint(row: any): ApiEndpoint {
    return {
      id: row.id,
      chainId: row.chain_id,
      endpointType: row.endpoint_type,
      endpointName: row.endpoint_name,
      endpointUrl: row.endpoint_url,
      isPrimary: row.is_primary,
      isActive: row.is_active,
      requiresAuth: row.requires_auth,
      authType: row.auth_type,
      authConfig: row.auth_config,
      rateLimitPerMinute: row.rate_limit_per_minute,
      timeoutMs: row.timeout_ms,
      healthCheckEnabled: row.health_check_enabled,
      lastHealthCheck: row.last_health_check,
      healthCheckStatus: row.health_check_status,
      createdBy: row.created_by,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  private mapDexFactory(row: any): DexFactoryConfig {
    return {
      id: row.id,
      chainId: row.chain_id,
      dexType: row.dex_type,
      factoryAddress: row.factory_address,
      routerAddress: row.router_address,
      poolManagerAddress: row.pool_manager_address,
      startBlock: parseInt(row.start_block, 10),
      isActive: row.is_active,
      description: row.description,
      createdBy: row.created_by,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  private mapAdminUser(row: any): AdminUser {
    return {
      id: row.id,
      username: row.username,
      email: row.email,
      passwordHash: row.password_hash,
      role: row.role,
      isActive: row.is_active,
      lastLogin: row.last_login,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}
