import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { IPackageParser, ParsedPackageInfo } from '../../domain/ports/package-parser.interface';

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

  async parse(buffer: Buffer, filename: string): Promise<ParsedPackageInfo> {
    const ext = filename.split('.').pop()?.toLowerCase();
    if (ext === 'apk') return this.parseApk(buffer);
    if (ext === 'ipa') return this.parseIpa(buffer);
    throw new BadRequestException('Unsupported file format. Only .apk and .ipa are supported.');
  }

  private async parseApk(buffer: Buffer): Promise<ParsedPackageInfo> {
    // yauzl.open() 不支持 APK V2/V3 签名格式（EOCD 被签名块挤移），
    // 改用 yauzl.fromBuffer() + adbkit-apkreader 内部 ManifestParser 直接解析
    try {
      const yauzl = await import('yauzl');
      // ManifestParser(buffer) 内部自带 BinaryXmlParser，直接传原始 Buffer 即可
      const ManifestParser: any = await import('adbkit-apkreader/lib/apkreader/parser/manifest' as any)
        .then((m: any) => m.default || m);

      const zipfile = await new Promise<any>((resolve, reject) =>
        yauzl.fromBuffer(buffer, { lazyEntries: true }, (err: any, zf: any) =>
          err ? reject(err) : resolve(zf),
        ),
      );

      const manifestBuffer = await new Promise<Buffer>((resolve, reject) => {
        zipfile.readEntry();
        zipfile.on('entry', (entry: any) => {
          if (entry.fileName === 'AndroidManifest.xml') {
            zipfile.openReadStream(entry, (err: any, stream: any) => {
              if (err) return reject(err);
              const chunks: Buffer[] = [];
              stream.on('data', (c: Buffer) => chunks.push(c));
              stream.on('end', () => resolve(Buffer.concat(chunks)));
              stream.on('error', reject);
            });
          } else {
            zipfile.readEntry();
          }
        });
        zipfile.on('end', () => reject(new Error('AndroidManifest.xml not found')));
        zipfile.on('error', reject);
      });

      const manifest = new ManifestParser(manifestBuffer).parse();

      return {
        packageName: manifest.package || 'unknown',
        versionCode: manifest.versionCode || 0,
        versionName: manifest.versionName || '0.0.0',
        minSdkVersion: manifest.usesSdk?.minSdkVersion?.toString(),
        platform: 'ANDROID',
      };
    } catch (err: any) {
      this.logger.warn(`APK parse failed, using fallback: ${err.message}`);
      return {
        packageName: 'unknown',
        versionCode: 0,
        versionName: '0.0.0',
        platform: 'ANDROID',
      };
    }
  }

  private async parseIpa(buffer: Buffer): Promise<ParsedPackageInfo> {
    try {
      const unzipper = await import('unzipper');
      const bplistParser = await import('bplist-parser');
      const directory = await unzipper.Open.buffer(buffer);
      const plistEntry = directory.files.find((f: any) => /Payload\/[^/]+\.app\/Info\.plist$/.test(f.path));
      if (!plistEntry) throw new Error('Info.plist not found in IPA');

      const plistBuffer = await plistEntry.buffer();
      const parsed = bplistParser.parseBuffer(plistBuffer);
      const info = parsed[0] || {};

      return {
        packageName: info.CFBundleIdentifier || 'unknown',
        versionCode: parseInt(info.CFBundleVersion || '0', 10),
        versionName: info.CFBundleShortVersionString || '0.0.0',
        minSdkVersion: info.MinimumOSVersion,
        platform: 'IOS',
      };
    } catch (err: any) {
      this.logger.warn(`IPA parse failed, using fallback: ${err.message}`);
      return {
        packageName: 'unknown',
        versionCode: 0,
        versionName: '0.0.0',
        platform: 'IOS',
      };
    }
  }
}
