/**
 * Auto-Update Service — prueft und installiert App-Updates via RVS
 *
 * Flow:
 * 1. App sendet "update_check" mit aktueller Version an RVS
 * 2. RVS vergleicht → sendet "update_available" mit Download-URL
 * 3. App zeigt Benachrichtigung → User bestaetigt → Download + Install
 */

import { Alert, Linking, Platform, NativeModules } from 'react-native';
import RNFS from 'react-native-fs';
import rvs, { RVSMessage } from './rvs';

// Version aus package.json (wird beim Build eingebettet)
const packageJson = require('../../package.json');
const APP_VERSION = packageJson.version || '0.0.0.0';

type UpdateCallback = (info: UpdateInfo) => void;

export interface UpdateInfo {
  version: string;
  downloadUrl: string;
  size: number;
}

class UpdateService {
  private listeners: UpdateCallback[] = [];
  private checking = false;
  private downloading = false;

  constructor() {
    // Beim Start alte APK-Reste aus dem Cache wegraeumen — wenn diese App
    // laeuft, sind frueher heruntergeladene APKs entweder schon installiert
    // oder unvollstaendig gewesen. Spart sonst pro Update 20-30MB auf dem Handy.
    this.cleanupOldApks().catch(() => {});

    // Auf update_available Nachrichten lauschen
    rvs.onMessage((msg: RVSMessage) => {
      if (msg.type === 'update_available' as any) {
        const info: UpdateInfo = {
          version: (msg.payload.version as string) || '',
          downloadUrl: (msg.payload.downloadUrl as string) || '',
          size: (msg.payload.size as number) || 0,
        };
        if (info.version && this.isNewer(info.version)) {
          console.log(`[Update] Neue Version verfuegbar: ${info.version} (aktuell: ${APP_VERSION})`);
          this.listeners.forEach(cb => cb(info));
        }
      }
    });
  }

  /** Sucht ueberall wo .apk-Dateien herumliegen koennten. */
  private async _apkSearchDirs(): Promise<string[]> {
    const dirs = [RNFS.CachesDirectoryPath, RNFS.DocumentDirectoryPath];
    if ((RNFS as any).ExternalCachesDirectoryPath) {
      dirs.push((RNFS as any).ExternalCachesDirectoryPath);
    }
    if (RNFS.ExternalDirectoryPath) {
      dirs.push(RNFS.ExternalDirectoryPath);
    }
    return dirs;
  }

  /** Raeumt alte heruntergeladene APK-Dateien aus den App-Verzeichnissen auf.
   *  Public damit Settings den Button "Update-Cache leeren" benutzen kann. */
  async cleanupOldApks(keepCurrentName?: string): Promise<{ removed: number; freedMB: number }> {
    const dirs = await this._apkSearchDirs();
    let removed = 0;
    let freed = 0;
    for (const dir of dirs) {
      try {
        if (!(await RNFS.exists(dir))) continue;
        const files = await RNFS.readDir(dir);
        const apks = files.filter(f => /\.apk$/i.test(f.name));
        for (const f of apks) {
          if (keepCurrentName && f.name === keepCurrentName) continue;
          try {
            const size = parseInt(f.size as any, 10) || 0;
            await RNFS.unlink(f.path);
            removed += 1;
            freed += size;
            console.log(`[Update] APK geloescht: ${f.path} (${(size / 1024 / 1024).toFixed(1)}MB)`);
          } catch (err: any) {
            console.warn(`[Update] APK-Loeschen fehlgeschlagen: ${f.path} (${err?.message || err})`);
          }
        }
      } catch (err: any) {
        console.warn(`[Update] Cleanup-Fehler in ${dir}: ${err?.message || err}`);
      }
    }
    const freedMB = freed / 1024 / 1024;
    if (removed > 0) {
      console.log(`[Update] Cleanup fertig: ${removed} APK${removed === 1 ? '' : 's'} entfernt, ${freedMB.toFixed(1)}MB freigegeben`);
    }
    return { removed, freedMB };
  }

  /** Aktuelle Groesse aller APK-Dateien in den App-Verzeichnissen (in MB). */
  async getApkCacheSize(): Promise<{ count: number; totalMB: number }> {
    const dirs = await this._apkSearchDirs();
    let count = 0;
    let total = 0;
    for (const dir of dirs) {
      try {
        if (!(await RNFS.exists(dir))) continue;
        const files = await RNFS.readDir(dir);
        for (const f of files) {
          if (!f.isFile() || !/\.apk$/i.test(f.name)) continue;
          count += 1;
          total += parseInt(f.size as any, 10) || 0;
        }
      } catch {}
    }
    return { count, totalMB: total / 1024 / 1024 };
  }

  /** Bei App-Start Update pruefen */
  checkForUpdate(): void {
    if (this.checking) return;
    this.checking = true;

    console.log(`[Update] Pruefe auf Updates (aktuell: ${APP_VERSION})`);
    rvs.send('update_check' as any, { version: APP_VERSION });

    setTimeout(() => { this.checking = false; }, 10000);
  }

  /** Callback registrieren */
  onUpdateAvailable(callback: UpdateCallback): () => void {
    this.listeners.push(callback);
    return () => {
      this.listeners = this.listeners.filter(cb => cb !== callback);
    };
  }

  /** Update-Dialog anzeigen */
  promptUpdate(info: UpdateInfo): void {
    const sizeMB = (info.size / 1024 / 1024).toFixed(1);
    Alert.alert(
      'ARIA Update verfuegbar',
      `Version ${info.version} (${sizeMB} MB)\n\nAktuell: ${APP_VERSION}\n\nJetzt herunterladen und installieren?`,
      [
        { text: 'Spaeter', style: 'cancel' },
        {
          text: 'Installieren',
          onPress: () => this.downloadAndInstall(info),
        },
      ],
    );
  }

  /** APK ueber WebSocket herunterladen und installieren */
  async downloadAndInstall(info: UpdateInfo): Promise<void> {
    if (this.downloading) return;
    this.downloading = true;

    try {
      console.log(`[Update] Fordere APK v${info.version} an...`);
      Alert.alert('Download gestartet', `Version ${info.version} wird ueber RVS heruntergeladen...`);

      // APK ueber WebSocket anfordern
      rvs.send('update_download' as any, {});

      // Auf update_data warten (einmalig)
      const apkData = await new Promise<{base64: string, fileName: string}>((resolve, reject) => {
        const timeout = setTimeout(() => reject(new Error('Download-Timeout (60s)')), 60000);
        const unsub = rvs.onMessage((msg: RVSMessage) => {
          if ((msg.type as string) === 'update_data') {
            clearTimeout(timeout);
            unsub();
            if (msg.payload.error) {
              reject(new Error(msg.payload.error as string));
            } else {
              resolve({
                base64: msg.payload.base64 as string,
                fileName: msg.payload.fileName as string || `ARIA-${info.version}.apk`,
              });
            }
          }
        });
      });

      // Vor dem Schreiben alte APKs im Cache wegraeumen — falls mehrere
      // Updates in einer Session gezogen werden
      await this.cleanupOldApks();

      // Base64 als APK-Datei speichern
      const destPath = `${RNFS.CachesDirectoryPath}/${apkData.fileName}`;
      await RNFS.writeFile(destPath, apkData.base64, 'base64');
      const fileSize = await RNFS.stat(destPath);
      console.log(`[Update] APK gespeichert: ${destPath} (${(Number(fileSize.size) / 1024 / 1024).toFixed(1)}MB)`);

      // APK installieren via natives ApkInstaller Module (FileProvider + Intent)
      if (Platform.OS === 'android') {
        try {
          const { ApkInstaller } = NativeModules;
          await ApkInstaller.install(destPath);
        } catch (installErr: any) {
          Alert.alert(
            'APK heruntergeladen',
            `Version ${info.version} gespeichert.\n\nBitte manuell installieren:\nDateimanager → ${apkData.fileName} antippen.\n\n(${installErr.message})`,
          );
        }
      }
    } catch (err: any) {
      console.error(`[Update] Fehler: ${err.message}`);
      Alert.alert('Update fehlgeschlagen', err.message);
    } finally {
      this.downloading = false;
    }
  }

  /** Versionsvergleich */
  private isNewer(remote: string): boolean {
    const r = remote.split('.').map(Number);
    const l = APP_VERSION.split('.').map(Number);
    for (let i = 0; i < Math.max(r.length, l.length); i++) {
      const diff = (r[i] || 0) - (l[i] || 0);
      if (diff > 0) return true;
      if (diff < 0) return false;
    }
    return false;
  }

  getCurrentVersion(): string {
    return APP_VERSION;
  }
}

const updateService = new UpdateService();
export default updateService;
