import { Injectable, Logger } from '@nestjs/common';
import { GembaDocsConnector, ConnectorHealth } from './gembadocs.connector';
import {
  GembaAudit,
  GembaChecklist,
  GembaFinding,
  GembaStatistics,
  GembaTrendData,
  ListAuditsDto,
  CreateAuditDto,
  UpdateAuditDto,
  ListFindingsDto,
  UpdateFindingDto,
  ResolveFindingDto,
  GetTrendsDto,
  AuditResponseDto,
  AuditDetailResponseDto,
  FindingResponseDto,
  ChecklistResponseDto,
  ChecklistDetailResponseDto,
  StatisticsResponseDto,
  TrendResponseDto,
  PaginatedAuditsResponseDto,
  PaginatedFindingsResponseDto,
  FindingStatus,
  FindingSeverity,
} from './gembadocs.types';

/**
 * GembaDocs Service
 *
 * High-level service for interacting with GembaDocs audit and checklist management system.
 * Wraps the connector with additional business logic, data transformation, and logging.
 */
@Injectable()
export class GembaDocsService {
  private readonly logger = new Logger(GembaDocsService.name);

  constructor(private readonly connector: GembaDocsConnector) {}

  // ============ Health & Status ============

  /**
   * Check health status of the GembaDocs integration
   */
  async checkHealth(): Promise<ConnectorHealth> {
    this.logger.debug('Checking GembaDocs connector health');
    return this.connector.checkHealth();
  }

  /**
   * Test connection to GembaDocs
   */
  async testConnection(): Promise<{ success: boolean; message: string; latency?: number }> {
    this.logger.log('Testing GembaDocs connection');
    const result = await this.connector.testConnection();
    this.logger.log(
      `GembaDocs connection test: ${result.success ? 'SUCCESS' : 'FAILED'} - ${result.message}`,
    );
    return result;
  }

  /**
   * Check if connector is configured
   */
  isConfigured(): boolean {
    return this.connector.isConfigured();
  }

  /**
   * Get missing configuration items
   */
  getMissingConfig(): string[] {
    return this.connector.getMissingConfig();
  }

  // ============ Audits ============

  /**
   * List audits with optional filters
   */
  async listAudits(params: ListAuditsDto = {}): Promise<PaginatedAuditsResponseDto> {
    this.logger.debug(`Listing audits with params: ${JSON.stringify(params)}`);

    const result = await this.connector.listAudits(params);

    this.logger.debug(`Found ${result.audits.length} audits`);

    return {
      audits: result.audits.map((audit) => this.transformAuditToResponse(audit)),
      total: result.pagination.total,
      page: result.pagination.page,
      pageSize: result.pagination.pageSize,
      totalPages: result.pagination.totalPages,
    };
  }

  /**
   * Get a single audit by ID
   */
  async getAudit(id: string): Promise<AuditDetailResponseDto> {
    this.logger.debug(`Getting audit: ${id}`);
    const audit = await this.connector.getAudit(id);
    return this.transformAuditToDetailResponse(audit);
  }

  /**
   * Get upcoming audits
   */
  async getUpcomingAudits(days: number = 7, limit: number = 10): Promise<AuditResponseDto[]> {
    this.logger.debug(`Getting upcoming audits for next ${days} days`);
    const audits = await this.connector.getUpcomingAudits(days, limit);
    this.logger.debug(`Found ${audits.length} upcoming audits`);
    return audits.map((audit) => this.transformAuditToResponse(audit));
  }

  /**
   * Get overdue audits
   */
  async getOverdueAudits(limit: number = 10): Promise<AuditResponseDto[]> {
    this.logger.debug('Getting overdue audits');
    const audits = await this.connector.getOverdueAudits(limit);
    this.logger.debug(`Found ${audits.length} overdue audits`);
    return audits.map((audit) => this.transformAuditToResponse(audit));
  }

  /**
   * Create a new audit
   */
  async createAudit(data: CreateAuditDto): Promise<AuditDetailResponseDto> {
    this.logger.log(`Creating audit: ${data.title}`);
    const audit = await this.connector.createAudit(data);
    this.logger.log(`Created audit: ${audit.id}`);
    return this.transformAuditToDetailResponse(audit);
  }

  /**
   * Update an audit
   */
  async updateAudit(id: string, data: UpdateAuditDto): Promise<AuditDetailResponseDto> {
    this.logger.log(`Updating audit: ${id}`);
    const audit = await this.connector.updateAudit(id, data);
    this.logger.log(`Updated audit: ${audit.id}`);
    return this.transformAuditToDetailResponse(audit);
  }

  // ============ Checklists ============

  /**
   * List all checklist templates
   */
  async listChecklists(): Promise<ChecklistResponseDto[]> {
    this.logger.debug('Listing checklists');
    const checklists = await this.connector.listChecklists();
    this.logger.debug(`Found ${checklists.length} checklists`);
    return checklists.map((checklist) => this.transformChecklistToResponse(checklist));
  }

  /**
   * Get a single checklist by ID
   */
  async getChecklist(id: string): Promise<ChecklistDetailResponseDto> {
    this.logger.debug(`Getting checklist: ${id}`);
    const checklist = await this.connector.getChecklist(id);
    return this.transformChecklistToDetailResponse(checklist);
  }

  // ============ Findings ============

  /**
   * List findings with optional filters
   */
  async listFindings(params: ListFindingsDto = {}): Promise<PaginatedFindingsResponseDto> {
    this.logger.debug(`Listing findings with params: ${JSON.stringify(params)}`);

    const result = await this.connector.listFindings(params);

    this.logger.debug(`Found ${result.findings.length} findings`);

    return {
      findings: result.findings.map((finding) => this.transformFindingToResponse(finding)),
      total: result.pagination.total,
      page: result.pagination.page,
      pageSize: result.pagination.pageSize,
      totalPages: result.pagination.totalPages,
    };
  }

  /**
   * Get open findings
   */
  async getOpenFindings(limit: number = 20): Promise<FindingResponseDto[]> {
    this.logger.debug('Getting open findings');
    const findings = await this.connector.getOpenFindings(limit);
    this.logger.debug(`Found ${findings.length} open findings`);
    return findings.map((finding) => this.transformFindingToResponse(finding));
  }

  /**
   * Update a finding
   */
  async updateFinding(id: string, data: UpdateFindingDto): Promise<FindingResponseDto> {
    this.logger.log(`Updating finding: ${id}`);
    const finding = await this.connector.updateFinding(id, data);
    this.logger.log(`Updated finding: ${finding.id}`);
    return this.transformFindingToResponse(finding);
  }

  /**
   * Resolve a finding
   */
  async resolveFinding(id: string, data: ResolveFindingDto): Promise<FindingResponseDto> {
    this.logger.log(`Resolving finding: ${id}`);
    const finding = await this.connector.resolveFinding(id, data);
    this.logger.log(`Resolved finding: ${finding.id}`);
    return this.transformFindingToResponse(finding);
  }

  /**
   * Get critical findings count (high and critical severity, open status)
   */
  async getCriticalFindingsCount(): Promise<number> {
    this.logger.debug('Getting critical findings count');

    // Get high severity findings
    const highResult = await this.connector.listFindings({
      severity: FindingSeverity.HIGH,
      status: FindingStatus.OPEN,
      pageSize: 1,
    });

    // Get critical severity findings
    const criticalResult = await this.connector.listFindings({
      severity: FindingSeverity.CRITICAL,
      status: FindingStatus.OPEN,
      pageSize: 1,
    });

    const count = highResult.pagination.total + criticalResult.pagination.total;
    this.logger.debug(`Found ${count} critical/high severity open findings`);
    return count;
  }

  // ============ Statistics & Trends ============

  /**
   * Get audit statistics
   */
  async getStatistics(department?: string): Promise<StatisticsResponseDto> {
    this.logger.debug(`Getting statistics${department ? ` for department: ${department}` : ''}`);
    const stats = await this.connector.getStatistics(department);
    return this.transformStatisticsToResponse(stats);
  }

  /**
   * Get trend data for charts
   */
  async getTrends(params: GetTrendsDto = {}): Promise<TrendResponseDto> {
    this.logger.debug(`Getting trends with params: ${JSON.stringify(params)}`);
    const trends = await this.connector.getTrends(params);
    return {
      period: trends.period,
      startDate: trends.startDate,
      endDate: trends.endDate,
      dataPoints: trends.dataPoints,
    };
  }

  /**
   * Get compliance score (percentage of completed audits with score >= threshold)
   */
  async getComplianceScore(threshold: number = 80): Promise<{
    score: number;
    trend: 'up' | 'down' | 'stable';
    change: number;
  }> {
    this.logger.debug(`Calculating compliance score with threshold: ${threshold}`);
    const stats = await this.connector.getStatistics();

    // Calculate trend based on historical data
    const complianceTrend = stats.complianceTrend || [];
    let trend: 'up' | 'down' | 'stable' = 'stable';
    let change = 0;

    if (complianceTrend.length >= 2) {
      const latest = complianceTrend[complianceTrend.length - 1];
      const previous = complianceTrend[complianceTrend.length - 2];
      change = latest.score - previous.score;

      if (change > 1) {
        trend = 'up';
      } else if (change < -1) {
        trend = 'down';
      }
    }

    return {
      score: stats.averageComplianceScore,
      trend,
      change: Math.round(change * 10) / 10,
    };
  }

  // ============ Data Transformers ============

  private transformAuditToResponse(audit: GembaAudit): AuditResponseDto {
    return {
      id: audit.id,
      title: audit.title,
      type: audit.type,
      status: audit.status,
      scheduledDate: audit.scheduledDate,
      completedDate: audit.completedDate,
      auditor: {
        id: audit.auditor.id,
        name: audit.auditor.name,
        email: audit.auditor.email,
        department: audit.auditor.department,
      },
      department: audit.department,
      location: audit.location,
      score: audit.score,
      maxScore: audit.maxScore,
      findingsCount: audit.findings?.length || 0,
      createdAt: audit.createdAt,
      updatedAt: audit.updatedAt,
    };
  }

  private transformAuditToDetailResponse(audit: GembaAudit): AuditDetailResponseDto {
    return {
      ...this.transformAuditToResponse(audit),
      checklist: audit.checklist,
      findings: audit.findings || [],
      notes: audit.notes,
    };
  }

  private transformFindingToResponse(finding: GembaFinding): FindingResponseDto {
    const isOverdue =
      finding.dueDate &&
      new Date(finding.dueDate) < new Date() &&
      finding.status !== FindingStatus.RESOLVED &&
      finding.status !== FindingStatus.VERIFIED;

    return {
      id: finding.id,
      auditId: finding.auditId,
      title: finding.title,
      description: finding.description,
      severity: finding.severity,
      status: finding.status,
      dueDate: finding.dueDate,
      assignee: finding.assignee
        ? {
            id: finding.assignee.id,
            name: finding.assignee.name,
            email: finding.assignee.email,
            department: finding.assignee.department,
          }
        : undefined,
      photos: finding.photos || [],
      correctiveAction: finding.correctiveAction,
      rootCause: finding.rootCause,
      createdAt: finding.createdAt,
      updatedAt: finding.updatedAt,
      resolvedAt: finding.resolvedAt,
      isOverdue: !!isOverdue,
    };
  }

  private transformChecklistToResponse(checklist: GembaChecklist): ChecklistResponseDto {
    return {
      id: checklist.id,
      name: checklist.name,
      version: checklist.version,
      description: checklist.description,
      totalItems: checklist.totalItems,
      categoryCount: checklist.categories?.length || 0,
      createdAt: checklist.createdAt,
      updatedAt: checklist.updatedAt,
    };
  }

  private transformChecklistToDetailResponse(
    checklist: GembaChecklist,
  ): ChecklistDetailResponseDto {
    return {
      ...this.transformChecklistToResponse(checklist),
      categories: checklist.categories || [],
    };
  }

  private transformStatisticsToResponse(stats: GembaStatistics): StatisticsResponseDto {
    // Calculate compliance score change from trend
    let complianceScoreChange = 0;
    if (stats.complianceTrend && stats.complianceTrend.length >= 2) {
      const latest = stats.complianceTrend[stats.complianceTrend.length - 1];
      const previous = stats.complianceTrend[stats.complianceTrend.length - 2];
      complianceScoreChange = Math.round((latest.score - previous.score) * 10) / 10;
    }

    return {
      totalAudits: stats.totalAudits,
      completedAudits: stats.completedAudits,
      scheduledAudits: stats.scheduledAudits,
      overdueAudits: stats.overdueAudits,
      totalFindings: stats.totalFindings,
      openFindings: stats.openFindings,
      resolvedFindings: stats.resolvedFindings,
      averageComplianceScore: stats.averageComplianceScore,
      complianceScoreChange,
      auditsByType: stats.auditsByType as Record<string, number>,
      findingsBySeverity: stats.findingsBySeverity as Record<string, number>,
      findingsByStatus: stats.findingsByStatus as Record<string, number>,
    };
  }
}
