import pino from 'pino';
import type { DiscoveredService } from './aws-scanner.js';
import type { GitHubRepo } from './github-scanner.js';

const logger = pino({ name: 'catalog' });

// --- Ownership Resolution (BMad must-have: explicit > implicit > heuristic) ---

export type OwnerSource = 'config' | 'codeowners' | 'aws-tag' | 'heuristic';

export interface OwnershipRecord {
  owner: string;
  source: OwnerSource;
  confidence: number; // 0-1
}

const SOURCE_PRIORITY: Record<OwnerSource, number> = {
  config: 4,
  codeowners: 3,
  'aws-tag': 2,
  heuristic: 1,
};

export function resolveOwnership(candidates: OwnershipRecord[]): OwnershipRecord {
  if (candidates.length === 0) {
    return { owner: 'unknown', source: 'heuristic', confidence: 0 };
  }
  // Highest priority source wins
  return candidates.sort((a, b) => SOURCE_PRIORITY[b.source] - SOURCE_PRIORITY[a.source])[0];
}

// --- Catalog Service ---

export interface CatalogEntry {
  id: string;
  tenantId: string;
  name: string;
  type: string;
  owner: string;
  ownerSource: OwnerSource;
  description?: string;
  tier?: 'critical' | 'high' | 'medium' | 'low';
  lifecycle?: 'active' | 'deprecated' | 'decommissioned';
  links: Record<string, string>; // repo, dashboard, runbook, etc.
  tags: Record<string, string>;
  metadata: Record<string, any>;
  lastDiscoveredAt: Date;
  createdAt: Date;
  updatedAt: Date;
}

export interface StagedUpdate {
  serviceName: string;
  changes: Partial<CatalogEntry>;
  source: 'aws' | 'github' | 'manual';
}

/**
 * Catalog service handles merging discovery results into the catalog.
 * Partial scan failures stage results without committing (BMad must-have).
 */
export class CatalogService {
  private pool: any;

  constructor(pool: any) {
    this.pool = pool;
  }

  async mergeAwsDiscovery(tenantId: string, services: DiscoveredService[], isPartial: boolean): Promise<number> {
    if (isPartial) {
      // Stage results — don't delete or overwrite existing entries
      logger.warn({ tenantId, count: services.length }, 'Partial scan — staging results');
      return this.stageUpdates(tenantId, services.map(s => ({
        serviceName: s.name,
        changes: {
          type: s.type,
          tags: s.tags,
          owner: s.owner,
          ownerSource: s.ownerSource as OwnerSource,
          lastDiscoveredAt: s.discoveredAt,
        },
        source: 'aws' as const,
      })));
    }

    // Full scan — upsert all discovered services
    let upserted = 0;
    for (const svc of services) {
      await this.upsertService(tenantId, {
        name: svc.name,
        type: svc.type,
        tags: svc.tags,
        metadata: svc.metadata,
        owner: svc.owner ?? 'unknown',
        ownerSource: (svc.ownerSource as OwnerSource) ?? 'aws-tag',
        lastDiscoveredAt: svc.discoveredAt,
      });
      upserted++;
    }
    return upserted;
  }

  async mergeGitHubDiscovery(tenantId: string, repos: GitHubRepo[], isPartial: boolean): Promise<number> {
    if (isPartial) {
      return this.stageUpdates(tenantId, repos.map(r => ({
        serviceName: r.name,
        changes: {
          owner: r.owner,
          ownerSource: r.ownerSource as OwnerSource,
          links: { repo: `https://github.com/${r.fullName}` },
          tags: { language: r.language },
        },
        source: 'github' as const,
      })));
    }

    let upserted = 0;
    for (const repo of repos) {
      // Only update ownership if GitHub source has higher priority
      const existing = await this.getService(tenantId, repo.name);
      if (existing) {
        const resolved = resolveOwnership([
          { owner: existing.owner, source: existing.ownerSource, confidence: 1 },
          { owner: repo.owner, source: repo.ownerSource as OwnerSource, confidence: 0.8 },
        ]);
        await this.updateOwner(tenantId, repo.name, resolved.owner, resolved.source);
      }
      upserted++;
    }
    return upserted;
  }

  private async stageUpdates(tenantId: string, updates: StagedUpdate[]): Promise<number> {
    const { withTenant } = await import('../data/db.js');
    await withTenant(tenantId, async (client) => {
      for (const update of updates) {
        await client.query(
          `INSERT INTO staged_updates (tenant_id, service_name, source, changes)
           VALUES ($1, $2, $3, $4)`,
          [tenantId, update.serviceName, update.source, JSON.stringify(update.changes)],
        );
      }
    });
    logger.info({ tenantId, count: updates.length }, 'Staged updates for review');
    return updates.length;
  }

  private async upsertService(tenantId: string, data: Partial<CatalogEntry>): Promise<void> {
    const { withTenant } = await import('../data/db.js');
    await withTenant(tenantId, async (client) => {
      await client.query(
        `INSERT INTO services (tenant_id, name, type, owner, owner_source, tags, metadata, last_discovered_at)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
         ON CONFLICT (tenant_id, name) DO UPDATE SET
           type = EXCLUDED.type, owner = EXCLUDED.owner, owner_source = EXCLUDED.owner_source,
           tags = EXCLUDED.tags, metadata = EXCLUDED.metadata,
           last_discovered_at = EXCLUDED.last_discovered_at, updated_at = now()`,
        [tenantId, data.name, data.type, data.owner, data.ownerSource, JSON.stringify(data.tags ?? {}), JSON.stringify(data.metadata ?? {}), data.lastDiscoveredAt],
      );
    });
  }

  private async getService(tenantId: string, name: string): Promise<CatalogEntry | null> {
    const { withTenant } = await import('../data/db.js');
    return withTenant(tenantId, async (client) => {
      const result = await client.query('SELECT * FROM services WHERE name = $1', [name]);
      return result.rows[0] ?? null;
    });
  }

  private async updateOwner(tenantId: string, name: string, owner: string, source: OwnerSource): Promise<void> {
    const { withTenant } = await import('../data/db.js');
    await withTenant(tenantId, async (client) => {
      await client.query(
        'UPDATE services SET owner = $1, owner_source = $2, updated_at = now() WHERE name = $3',
        [owner, source, name],
      );
    });
  }
}
