import { ADRStore } from './store.js';
import { ConfigManager } from '../../config/config-manager.js';
import type {
  ADR,
  CreateADROptions,
  UpdateADROptions,
  SearchADROptions,
  ADRMetrics,
  ADRRelationship,
  ADRReferenceValidation,
  ADRReferenceError,
  ADRStatus
} from './types.js';

export class ADRManager {
  private store: ADRStore;
  private configManager: ConfigManager;

  constructor(configManager: ConfigManager) {
    this.configManager = configManager;
    this.store = new ADRStore();
  }

  async initialize(): Promise<void> {
    await this.store.initialize();
  }

  async createADR(options: CreateADROptions): Promise<ADR> {
    const adrNumber = await this.store.getNextADRNumber();
    const now = new Date();
    
    const adr: ADR = {
      id: `ADR-${adrNumber}`,
      title: options.title,
      status: 'proposed',
      date: now,
      deciders: options.deciders,
      template: options.template,
      context: options.context,
      decision: options.decision,
      consequences: options.consequences,
      tags: options.tags,
      decisionDrivers: options.decisionDrivers,
      consideredOptions: options.consideredOptions,
      prosAndCons: options.prosAndCons,
      supersedes: options.supersedes,
      relatedTo: options.relatedTo,
      createdAt: now,
      updatedAt: now,
      statusHistory: []
    };

    await this.store.saveADR(adr);
    await this.updateIndex();

    return adr;
  }

  async updateADR(id: string, options: UpdateADROptions): Promise<ADR> {
    const adr = await this.store.loadADR(id);
    if (!adr) {
      throw new Error(`ADR ${id} not found`);
    }

    const previousStatus = adr.status;
    const now = new Date();

    // Update basic fields
    if (options.title !== undefined) adr.title = options.title;
    if (options.consequences !== undefined) adr.consequences = options.consequences;
    if (options.tags !== undefined) adr.tags = options.tags;
    if (options.decisionDrivers !== undefined) adr.decisionDrivers = options.decisionDrivers;
    if (options.consideredOptions !== undefined) adr.consideredOptions = options.consideredOptions;
    if (options.prosAndCons !== undefined) adr.prosAndCons = options.prosAndCons;

    // Handle status change
    if (options.status !== undefined && options.status !== previousStatus) {
      adr.status = options.status;
      adr.statusHistory.push({
        from: previousStatus,
        to: options.status,
        date: now,
        changedBy: options.statusChangedBy || 'Unknown',
        reason: options.statusChangeReason
      });
    }

    // Handle relationships
    if (options.supersedes !== undefined) {
      adr.supersedes = options.supersedes;
      
      // Update superseded ADRs
      for (const supersededId of options.supersedes) {
        const supersededADR = await this.store.loadADR(supersededId);
        if (supersededADR) {
          supersededADR.supersededBy = id;
          supersededADR.status = 'superseded';
          supersededADR.updatedAt = now;
          await this.store.saveADR(supersededADR);
        }
      }
    }

    if (options.supersededBy !== undefined) adr.supersededBy = options.supersededBy;
    if (options.relatedTo !== undefined) adr.relatedTo = options.relatedTo;

    adr.updatedAt = now;
    await this.store.saveADR(adr);
    await this.updateIndex();

    return adr;
  }

  async linkADRs(fromId: string, toId: string, relationship: ADRRelationship['type']): Promise<void> {
    const fromADR = await this.store.loadADR(fromId);
    const toADR = await this.store.loadADR(toId);

    if (!fromADR || !toADR) {
      throw new Error('One or both ADRs not found');
    }

    const now = new Date();

    switch (relationship) {
      case 'related-to':
        // Add bidirectional links
        if (!fromADR.relatedTo) fromADR.relatedTo = [];
        if (!toADR.relatedTo) toADR.relatedTo = [];
        
        if (!fromADR.relatedTo.includes(toId)) {
          fromADR.relatedTo.push(toId);
          fromADR.updatedAt = now;
          await this.store.saveADR(fromADR);
        }
        
        if (!toADR.relatedTo.includes(fromId)) {
          toADR.relatedTo.push(fromId);
          toADR.updatedAt = now;
          await this.store.saveADR(toADR);
        }
        break;

      case 'supersedes':
        // fromADR supersedes toADR
        if (!fromADR.supersedes) fromADR.supersedes = [];
        if (!fromADR.supersedes.includes(toId)) {
          fromADR.supersedes.push(toId);
          fromADR.updatedAt = now;
          await this.store.saveADR(fromADR);
        }

        toADR.supersededBy = fromId;
        toADR.status = 'superseded';
        toADR.updatedAt = now;
        await this.store.saveADR(toADR);
        break;

      default:
        throw new Error(`Unsupported relationship type: ${relationship}`);
    }

    await this.updateIndex();
  }

  async getADR(id: string): Promise<{ adr: ADR; markdown: string; relationships: ADRRelationship[] } | null> {
    const adr = await this.store.loadADR(id);
    if (!adr) {
      return null;
    }

    const markdown = await this.store.exportToMarkdown(adr);
    const relationships: ADRRelationship[] = [];

    // Build relationships
    if (adr.relatedTo) {
      for (const relatedId of adr.relatedTo) {
        relationships.push({
          type: 'related-to',
          from: id,
          to: relatedId
        });
      }
    }

    if (adr.supersedes) {
      for (const supersededId of adr.supersedes) {
        relationships.push({
          type: 'supersedes',
          from: id,
          to: supersededId
        });
      }
    }

    if (adr.supersededBy) {
      relationships.push({
        type: 'superseded-by',
        from: id,
        to: adr.supersededBy
      });
    }

    return { adr, markdown, relationships };
  }

  async listADRs(filter?: { status?: ADRStatus } | {}): Promise<ADR[]> {
    const adrIds = await this.store.listADRs();
    const adrs: ADR[] = [];

    for (const id of adrIds) {
      const adr = await this.store.loadADR(id);
      if (adr) {
        if (!filter || !('status' in filter) || !filter.status || adr.status === filter.status) {
          adrs.push(adr);
        }
      }
    }

    // Sort by date descending
    return adrs.sort((a, b) => b.date.getTime() - a.date.getTime());
  }

  async searchADRs(options: SearchADROptions): Promise<ADR[]> {
    return await this.store.searchADRs(options);
  }

  async deleteADR(id: string): Promise<void> {
    await this.store.deleteADR(id);
    await this.updateIndex();
  }

  async getMetrics(): Promise<ADRMetrics> {
    const adrIds = await this.store.listADRs();
    const adrs: ADR[] = [];

    for (const id of adrIds) {
      const adr = await this.store.loadADR(id);
      if (adr) adrs.push(adr);
    }

    const byStatus: Record<ADRStatus, number> = {
      proposed: 0,
      accepted: 0,
      rejected: 0,
      deprecated: 0,
      superseded: 0
    };

    const byTemplate = {
      nygard: 0,
      madr: 0,
      'y-statement': 0
    };

    const deciderCounts = new Map<string, number>();
    const decisionTimes: number[] = [];

    for (const adr of adrs) {
      byStatus[adr.status]++;
      byTemplate[adr.template]++;

      // Count deciders
      for (const decider of adr.deciders) {
        deciderCounts.set(decider, (deciderCounts.get(decider) || 0) + 1);
      }

      // Calculate decision time for accepted ADRs
      if (adr.status === 'accepted' && adr.statusHistory.length > 0) {
        const acceptedChange = adr.statusHistory.find(ch => ch.to === 'accepted');
        if (acceptedChange) {
          const decisionTime = acceptedChange.date.getTime() - adr.createdAt.getTime();
          decisionTimes.push(decisionTime / (1000 * 60 * 60 * 24)); // Convert to days
        }
      }
    }

    const averageDecisionTime = decisionTimes.length > 0
      ? decisionTimes.reduce((a, b) => a + b, 0) / decisionTimes.length
      : 0;

    const mostActiveDeciders = Array.from(deciderCounts.entries())
      .map(([name, count]) => ({ name, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 10);

    return {
      total: adrs.length,
      byStatus,
      byTemplate,
      averageDecisionTime,
      mostActiveDeciders,
      recentActivity: [] // TODO: Implement activity tracking
    };
  }

  async validateReferences(): Promise<ADRReferenceValidation> {
    const adrIds = await this.store.listADRs();
    const errors: ADRReferenceError[] = [];

    for (const id of adrIds) {
      const adr = await this.store.loadADR(id);
      if (!adr) continue;

      // Check related ADRs
      if (adr.relatedTo) {
        for (const relatedId of adr.relatedTo) {
          const relatedADR = await this.store.loadADR(relatedId);
          if (!relatedADR) {
            errors.push({
              adrId: id,
              type: 'related-to',
              targetId: relatedId,
              error: 'Target ADR not found'
            });
          }
        }
      }

      // Check superseded ADRs
      if (adr.supersedes) {
        for (const supersededId of adr.supersedes) {
          const supersededADR = await this.store.loadADR(supersededId);
          if (!supersededADR) {
            errors.push({
              adrId: id,
              type: 'supersedes',
              targetId: supersededId,
              error: 'Target ADR not found'
            });
          }
        }
      }

      // Check superseded by
      if (adr.supersededBy) {
        const supersedingADR = await this.store.loadADR(adr.supersededBy);
        if (!supersedingADR) {
          errors.push({
            adrId: id,
            type: 'superseded-by',
            targetId: adr.supersededBy,
            error: 'Target ADR not found'
          });
        }
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }

  async generateDecisionLog(): Promise<string> {
    return await this.store.generateADRLog();
  }

  private async updateIndex(): Promise<void> {
    const adrIds = await this.store.listADRs();
    const adrs = [];

    for (const id of adrIds) {
      const adr = await this.store.loadADR(id);
      if (adr) {
        adrs.push({
          id: adr.id,
          title: adr.title,
          status: adr.status,
          date: adr.date,
          tags: adr.tags
        });
      }
    }

    await this.store.saveIndex({
      lastUpdated: new Date(),
      totalADRs: adrs.length,
      adrs
    });
  }
}