/**
 * Document Version Service
 * Logica de negocio para control de versiones.
 *
 * @module Documents (MAE-016)
 */

import { Repository } from 'typeorm';
import { DocumentVersion } from '../entities/document-version.entity';
import { Document } from '../entities/document.entity';

export interface CreateVersionDto {
  documentId: string;
  versionNumber: string;
  versionLabel?: string;
  fileName: string;
  fileExtension: string;
  fileSizeBytes: number;
  mimeType?: string;
  checksumMd5?: string;
  checksumSha256?: string;
  storageProvider?: string;
  storageBucket?: string;
  storageKey: string;
  storageUrl?: string;
  thumbnailUrl?: string;
  previewUrl?: string;
  pageCount?: number;
  changeSummary?: string;
  changeType?: string;
  uploadSource?: string;
}

export class DocumentVersionService {
  constructor(
    private readonly versionRepository: Repository<DocumentVersion>,
    private readonly documentRepository: Repository<Document>,
  ) {}

  /**
   * Create a new document version
   */
  async create(
    tenantId: string,
    dto: CreateVersionDto,
    userId?: string,
    userName?: string,
  ): Promise<DocumentVersion> {
    // Get document to validate
    const document = await this.documentRepository.findOne({
      where: { id: dto.documentId, tenantId },
    });

    if (!document) {
      throw new Error('Documento no encontrado');
    }

    // Mark previous current version as superseded
    await this.versionRepository.update(
      { documentId: dto.documentId, status: 'current' },
      { status: 'superseded', supersededAt: new Date() },
    );

    // Create new version
    const version = this.versionRepository.create({
      tenantId,
      ...dto,
      status: 'current',
      uploadedById: userId,
      uploadedByName: userName,
    });

    const savedVersion = await this.versionRepository.save(version);

    // Update document with current version
    await this.documentRepository.update(
      { id: dto.documentId },
      {
        currentVersionId: savedVersion.id,
        currentVersionNumber: dto.versionNumber,
      },
    );

    return savedVersion;
  }

  /**
   * Get all versions of a document
   */
  async findByDocument(
    tenantId: string,
    documentId: string,
  ): Promise<DocumentVersion[]> {
    return this.versionRepository.find({
      where: { tenantId, documentId },
      order: { createdAt: 'DESC' },
    });
  }

  /**
   * Get current version of a document
   */
  async findCurrentVersion(
    tenantId: string,
    documentId: string,
  ): Promise<DocumentVersion | null> {
    return this.versionRepository.findOne({
      where: { tenantId, documentId, status: 'current' },
    });
  }

  /**
   * Get version by ID
   */
  async findById(
    tenantId: string,
    id: string,
  ): Promise<DocumentVersion | null> {
    return this.versionRepository.findOne({
      where: { tenantId, id },
      relations: ['document'],
    });
  }

  /**
   * Set version as current
   */
  async setAsCurrent(
    tenantId: string,
    versionId: string,
  ): Promise<DocumentVersion | null> {
    const version = await this.findById(tenantId, versionId);
    if (!version) {
      return null;
    }

    // Mark all other versions as superseded
    await this.versionRepository.update(
      { documentId: version.documentId, status: 'current' },
      { status: 'superseded', supersededAt: new Date() },
    );

    // Set this version as current
    version.status = 'current';
    version.supersededAt = undefined;
    version.supersededByVersionId = undefined;

    const savedVersion = await this.versionRepository.save(version);

    // Update document
    await this.documentRepository.update(
      { id: version.documentId },
      {
        currentVersionId: versionId,
        currentVersionNumber: version.versionNumber,
      },
    );

    return savedVersion;
  }

  /**
   * Archive a version
   */
  async archive(
    tenantId: string,
    versionId: string,
  ): Promise<DocumentVersion | null> {
    const version = await this.findById(tenantId, versionId);
    if (!version) {
      return null;
    }

    if (version.status === 'current') {
      throw new Error('No se puede archivar la version actual');
    }

    version.status = 'archived';
    return this.versionRepository.save(version);
  }

  /**
   * Update version processing status
   */
  async updateProcessingStatus(
    tenantId: string,
    versionId: string,
    options: {
      isProcessed?: boolean;
      ocrText?: string;
      extractedMetadata?: Record<string, any>;
      pageCount?: number;
      pageDimensions?: Record<string, any>;
    },
  ): Promise<DocumentVersion | null> {
    const version = await this.findById(tenantId, versionId);
    if (!version) {
      return null;
    }

    Object.assign(version, options);
    return this.versionRepository.save(version);
  }

  /**
   * Generate next version number
   */
  async generateNextVersionNumber(
    tenantId: string,
    documentId: string,
    changeType: 'major' | 'minor' | 'patch' = 'minor',
  ): Promise<string> {
    const currentVersion = await this.findCurrentVersion(tenantId, documentId);

    if (!currentVersion) {
      return '1.0';
    }

    const parts = currentVersion.versionNumber.split('.').map(Number);
    const major = parts[0] || 1;
    const minor = parts[1] || 0;
    const patch = parts[2] || 0;

    switch (changeType) {
      case 'major':
        return `${major + 1}.0`;
      case 'minor':
        return `${major}.${minor + 1}`;
      case 'patch':
        return `${major}.${minor}.${patch + 1}`;
      default:
        return `${major}.${minor + 1}`;
    }
  }

  /**
   * Get version statistics
   */
  async getStatistics(
    tenantId: string,
    documentId: string,
  ): Promise<{
    totalVersions: number;
    totalSizeBytes: number;
    firstVersion: Date | null;
    lastVersion: Date | null;
  }> {
    const stats = await this.versionRepository
      .createQueryBuilder('v')
      .select('COUNT(*)', 'totalVersions')
      .addSelect('COALESCE(SUM(v.file_size_bytes), 0)', 'totalSizeBytes')
      .addSelect('MIN(v.created_at)', 'firstVersion')
      .addSelect('MAX(v.created_at)', 'lastVersion')
      .where('v.tenant_id = :tenantId', { tenantId })
      .andWhere('v.document_id = :documentId', { documentId })
      .getRawOne();

    return {
      totalVersions: parseInt(stats?.totalVersions || '0', 10),
      totalSizeBytes: parseInt(stats?.totalSizeBytes || '0', 10),
      firstVersion: stats?.firstVersion || null,
      lastVersion: stats?.lastVersion || null,
    };
  }

  /**
   * Compare two versions (metadata only)
   */
  async compareVersions(
    tenantId: string,
    versionId1: string,
    versionId2: string,
  ): Promise<{
    version1: DocumentVersion | null;
    version2: DocumentVersion | null;
    differences: string[];
  }> {
    const [v1, v2] = await Promise.all([
      this.findById(tenantId, versionId1),
      this.findById(tenantId, versionId2),
    ]);

    const differences: string[] = [];

    if (v1 && v2) {
      if (v1.fileSizeBytes !== v2.fileSizeBytes) {
        differences.push('Tamaño de archivo diferente');
      }
      if (v1.pageCount !== v2.pageCount) {
        differences.push('Número de páginas diferente');
      }
      if (v1.checksumMd5 !== v2.checksumMd5) {
        differences.push('Contenido modificado');
      }
    }

    return { version1: v1, version2: v2, differences };
  }
}
