import { Injectable, NotFoundException, ConflictException, Inject, Logger } from '@nestjs/common';
import { APP_VERSION_REPOSITORY, IAppVersionRepository } from '../../domain/repositories/app-version.repository.interface';
import { Platform } from '../../domain/enums/platform.enum';
import { AppType } from '../../domain/enums/app-type.enum';

@Injectable()
export class AppVersionService {
  private readonly logger = new Logger(AppVersionService.name);

  constructor(
    @Inject(APP_VERSION_REPOSITORY) private readonly versionRepo: IAppVersionRepository,
  ) {}

  /** Check for update - mobile client API */
  async checkUpdate(appType: AppType, platform: Platform, currentVersionCode: number) {
    const latest = await this.versionRepo.findLatestEnabled(appType, platform);

    if (!latest || latest.versionCode <= currentVersionCode) {
      return { needUpdate: false };
    }

    return {
      needUpdate: true,
      forceUpdate: latest.isForceUpdate && latest.isEnabled,
      version: latest.versionName,
      versionCode: latest.versionCode,
      // Use stable API download endpoint; storageKey versions generate fresh presigned URL on download
      downloadUrl: latest.storageKey
        ? `/api/v1/app/version/download/${latest.id}`
        : latest.downloadUrl,
      fileSize: Number(latest.fileSize),
      fileSizeFriendly: this.formatFileSize(BigInt(latest.fileSize)),
      sha256: latest.fileSha256,
      updateLog: latest.changelog,
      minOsVersion: latest.minOsVersion,
      releaseDate: latest.releaseDate?.toISOString() || null,
    };
  }

  /** List versions (admin) */
  async listVersions(appType?: AppType, platform?: Platform, includeDisabled = false) {
    return this.versionRepo.findByFilters(appType, platform, includeDisabled);
  }

  /** Get version detail */
  async getVersion(id: string) {
    const version = await this.versionRepo.findById(id);
    if (!version) throw new NotFoundException('Version not found');
    return version;
  }

  /** Create version (admin) */
  async createVersion(data: {
    appType: AppType;
    platform: Platform;
    versionCode: number;
    versionName: string;
    buildNumber: string;
    downloadUrl: string;
    storageKey?: string;
    fileSize: string;
    fileSha256: string;
    changelog: string;
    isForceUpdate: boolean;
    minOsVersion?: string;
    releaseDate?: Date;
    createdBy?: string;
  }) {
    // Check duplicate within same appType + platform
    const existing = await this.versionRepo.findByPlatformAndCode(
      data.appType, data.platform, data.versionCode,
    );
    if (existing) {
      throw new ConflictException(
        `Version code ${data.versionCode} already exists for ${data.appType}/${data.platform}`,
      );
    }

    const version = this.versionRepo.create({
      ...data,
      isEnabled: true,
    });
    return this.versionRepo.save(version);
  }

  /** Update version (admin) */
  async updateVersion(id: string, data: Partial<{
    downloadUrl: string;
    fileSize: string;
    fileSha256: string;
    changelog: string;
    isForceUpdate: boolean;
    minOsVersion: string;
    releaseDate: Date;
    updatedBy: string;
  }>) {
    const version = await this.getVersion(id);
    Object.assign(version, data);
    return this.versionRepo.save(version);
  }

  /** Toggle enable/disable */
  async toggleVersion(id: string, isEnabled: boolean) {
    await this.getVersion(id); // Verify exists
    await this.versionRepo.updatePartial(id, { isEnabled });
    return { success: true };
  }

  /** Delete version */
  async deleteVersion(id: string) {
    await this.getVersion(id); // Verify exists
    await this.versionRepo.deleteById(id);
    return { success: true };
  }

  private formatFileSize(bytes: bigint): string {
    const n = Number(bytes);
    if (n < 1024) return `${n} B`;
    if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
    if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
    return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
  }
}
