import { Injectable, Inject, NotFoundException, ConflictException, BadRequestException } from '@nestjs/common'
import {
  AppVersion,
  AppVersionRepository,
  APP_VERSION_REPOSITORY,
  Platform,
  VersionCode,
  VersionName,
  BuildNumber,
  DownloadUrl,
  FileSize,
  FileSha256,
  Changelog,
  MinOsVersion,
} from '../../domain/version-management'
import { FileStorageService } from '../../infrastructure/storage/file-storage.service'
import { PackageParserService } from '../../infrastructure/parsers/package-parser.service'

export interface CreateVersionInput {
  platform: Platform
  versionCode: number
  versionName: string
  buildNumber: string
  downloadUrl: string
  fileSize: string
  fileSha256: string
  changelog: string
  isForceUpdate: boolean
  minOsVersion?: string
  releaseDate?: Date
  createdBy: string
}

export interface UploadVersionInput {
  platform: Platform
  fileBuffer: Buffer
  originalFilename: string
  changelog: string
  isForceUpdate: boolean
  createdBy: string
  versionCode?: number
  versionName?: string
  buildNumber?: string
  minOsVersion?: string
  releaseDate?: Date
}

export interface UpdateVersionInput {
  downloadUrl?: string
  fileSize?: string
  fileSha256?: string
  changelog?: string
  isForceUpdate?: boolean
  minOsVersion?: string | null
  releaseDate?: Date | null
  updatedBy: string
}

export interface CheckUpdateResult {
  hasUpdate: boolean
  isForceUpdate: boolean
  latestVersion: {
    versionCode: number
    versionName: string
    buildNumber: string
    downloadUrl: string
    fileSize: string
    fileSizeFriendly: string
    fileSha256: string
    changelog: string
    minOsVersion: string | null
    releaseDate: Date | null
  } | null
}

@Injectable()
export class VersionService {
  constructor(
    @Inject(APP_VERSION_REPOSITORY)
    private readonly appVersionRepository: AppVersionRepository,
    private readonly fileStorageService: FileStorageService,
    private readonly packageParserService: PackageParserService,
  ) {}

  async create(input: CreateVersionInput): Promise<AppVersion> {
    // 检查版本是否已存在
    const existing = await this.appVersionRepository.findByPlatformAndVersionCode(
      input.platform,
      VersionCode.create(input.versionCode),
    )
    if (existing) {
      throw new ConflictException(`Version ${input.versionCode} already exists for ${input.platform}`)
    }

    const appVersion = AppVersion.create({
      platform: input.platform,
      versionCode: VersionCode.create(input.versionCode),
      versionName: VersionName.create(input.versionName),
      buildNumber: BuildNumber.create(input.buildNumber),
      downloadUrl: DownloadUrl.create(input.downloadUrl),
      fileSize: FileSize.create(BigInt(input.fileSize)),
      fileSha256: FileSha256.create(input.fileSha256),
      changelog: Changelog.create(input.changelog),
      isForceUpdate: input.isForceUpdate,
      minOsVersion: input.minOsVersion ? MinOsVersion.create(input.minOsVersion) : null,
      releaseDate: input.releaseDate ?? null,
      createdBy: input.createdBy,
    })

    return this.appVersionRepository.save(appVersion)
  }

  async upload(input: UploadVersionInput): Promise<AppVersion> {
    // 解析包信息
    const parsedInfo = await this.packageParserService.parsePackage(
      input.fileBuffer,
      input.platform,
    )

    // 确定版本信息
    const versionCode = input.versionCode ?? parsedInfo?.versionCode
    const versionName = input.versionName ?? parsedInfo?.versionName
    const buildNumber = input.buildNumber ?? versionCode?.toString()
    const minOsVersion = input.minOsVersion ?? parsedInfo?.minSdkVersion

    if (!versionCode) {
      throw new BadRequestException('Unable to detect version code from package. Please provide it manually.')
    }
    if (!versionName) {
      throw new BadRequestException('Unable to detect version name from package. Please provide it manually.')
    }

    // 检查版本是否已存在
    const existing = await this.appVersionRepository.findByPlatformAndVersionCode(
      input.platform,
      VersionCode.create(versionCode),
    )
    if (existing) {
      throw new ConflictException(`Version ${versionCode} already exists for ${input.platform}`)
    }

    // 保存文件
    const uploadResult = await this.fileStorageService.saveFile(
      input.fileBuffer,
      input.originalFilename,
      input.platform,
      versionName,
    )

    const appVersion = AppVersion.create({
      platform: input.platform,
      versionCode: VersionCode.create(versionCode),
      versionName: VersionName.create(versionName),
      buildNumber: BuildNumber.create(buildNumber || versionCode.toString()),
      downloadUrl: DownloadUrl.create(uploadResult.url),
      fileSize: FileSize.create(BigInt(uploadResult.size)),
      fileSha256: FileSha256.create(uploadResult.sha256),
      changelog: Changelog.create(input.changelog),
      isForceUpdate: input.isForceUpdate,
      minOsVersion: minOsVersion ? MinOsVersion.create(minOsVersion) : null,
      releaseDate: input.releaseDate ?? null,
      createdBy: input.createdBy,
    })

    return this.appVersionRepository.save(appVersion)
  }

  async findById(id: string): Promise<AppVersion> {
    const version = await this.appVersionRepository.findById(id)
    if (!version) {
      throw new NotFoundException(`Version with id ${id} not found`)
    }
    return version
  }

  async findAll(platform?: Platform, includeDisabled = false): Promise<AppVersion[]> {
    if (platform) {
      return this.appVersionRepository.findAllByPlatform(platform, includeDisabled)
    }
    return this.appVersionRepository.findAll(includeDisabled)
  }

  async update(id: string, input: UpdateVersionInput): Promise<AppVersion> {
    const version = await this.findById(id)

    version.update({
      downloadUrl: input.downloadUrl ? DownloadUrl.create(input.downloadUrl) : undefined,
      fileSize: input.fileSize ? FileSize.create(BigInt(input.fileSize)) : undefined,
      fileSha256: input.fileSha256 ? FileSha256.create(input.fileSha256) : undefined,
      changelog: input.changelog ? Changelog.create(input.changelog) : undefined,
      isForceUpdate: input.isForceUpdate,
      minOsVersion: input.minOsVersion === null
        ? null
        : input.minOsVersion
          ? MinOsVersion.create(input.minOsVersion)
          : undefined,
      releaseDate: input.releaseDate,
      updatedBy: input.updatedBy,
    })

    return this.appVersionRepository.update(version)
  }

  async toggleEnabled(id: string, isEnabled: boolean, updatedBy: string): Promise<AppVersion> {
    const version = await this.findById(id)
    if (isEnabled) {
      version.enable(updatedBy)
    } else {
      version.disable(updatedBy)
    }
    return this.appVersionRepository.update(version)
  }

  async delete(id: string): Promise<void> {
    await this.findById(id) // 确保存在
    await this.appVersionRepository.delete(id)
  }

  async checkUpdate(platform: Platform, currentVersionCode: number): Promise<CheckUpdateResult> {
    const latestVersion = await this.appVersionRepository.findLatestByPlatform(platform)

    if (!latestVersion) {
      return {
        hasUpdate: false,
        isForceUpdate: false,
        latestVersion: null,
      }
    }

    const currentVersion = VersionCode.create(currentVersionCode)
    const hasUpdate = latestVersion.isNewerThan(currentVersion)

    return {
      hasUpdate,
      isForceUpdate: hasUpdate && latestVersion.shouldForceUpdate(),
      latestVersion: hasUpdate
        ? {
            versionCode: latestVersion.versionCode.value,
            versionName: latestVersion.versionName.value,
            buildNumber: latestVersion.buildNumber.value,
            downloadUrl: latestVersion.downloadUrl.value,
            fileSize: latestVersion.fileSize.bytes.toString(),
            fileSizeFriendly: latestVersion.fileSize.toFriendlyString(),
            fileSha256: latestVersion.fileSha256.value,
            changelog: latestVersion.changelog.value,
            minOsVersion: latestVersion.minOsVersion?.value ?? null,
            releaseDate: latestVersion.releaseDate,
          }
        : null,
    }
  }

  async parsePackage(
    fileBuffer: Buffer,
    platform: Platform,
  ): Promise<{
    packageName: string
    versionCode: number
    versionName: string
    minSdkVersion?: string
    targetSdkVersion?: string
  } | null> {
    return this.packageParserService.parsePackage(fileBuffer, platform)
  }
}
