import * as FileSystem from 'expo-file-system';
import * as DocumentPicker from 'expo-document-picker';
import { unzip } from 'react-native-zip-archive';

import { savePhotoRecords } from './photoStorage/photoStorageService';
import type { PhotoRecord } from './photoStorage/types';

declare const __DEV__: boolean;

function isDevRuntime() {
  return typeof __DEV__ !== 'undefined' && __DEV__;
}

function logDev(message: string, details: Record<string, unknown>) {
  if (isDevRuntime()) console.log(message, details);
}

function warnDev(message: string, details: Record<string, unknown>) {
  if (isDevRuntime()) console.warn(message, details);
}

const gardenStorageDir = `${FileSystem.documentDirectory || ''}pixiesprout-garden/`;
const photosDir = `${FileSystem.documentDirectory || ''}pixiesprout-photos/`;
const importTempDir = `${FileSystem.cacheDirectory || ''}pixiesprout-import/`;

export type ImportResult = {
  success: boolean;
  plantCount?: number;
  photoCount?: number;
  error?: string;
};

async function ensureDir(dir: string) {
  const info = await FileSystem.getInfoAsync(dir);
  if (!info.exists) {
    await FileSystem.makeDirectoryAsync(dir, { intermediates: true });
  }
}

async function readJsonSafe<T>(path: string, fallback: T): Promise<T> {
  try {
    const info = await FileSystem.getInfoAsync(path);
    if (!info.exists) return fallback;
    return JSON.parse(await FileSystem.readAsStringAsync(path)) as T;
  } catch {
    return fallback;
  }
}

async function copyFileIfExists(source: string, dest: string): Promise<boolean> {
  try {
    const info = await FileSystem.getInfoAsync(source);
    if (!info.exists) return false;
    // Ensure destination directory exists
    const destDir = dest.substring(0, dest.lastIndexOf('/'));
    await ensureDir(destDir);
    await FileSystem.copyAsync({ from: source, to: dest });
    return true;
  } catch {
    return false;
  }
}

export async function pickAndImport(): Promise<ImportResult> {
  try {
    // Pick a ZIP file
    const result = await DocumentPicker.getDocumentAsync({
      type: 'application/zip',
      copyToCacheDirectory: true,
    });

    if (result.canceled || !result.assets?.[0]) {
      return { success: false, error: 'Import cancelled.' };
    }

    const zipUri = result.assets[0].uri;
    return await importFromZip(zipUri);
  } catch (error) {
    const msg = error instanceof Error ? error.message : 'unknown error';
    warnDev('[Import] picker failed', { error: msg });
    return { success: false, error: msg };
  }
}

export async function importFromZip(zipUri: string): Promise<ImportResult> {
  try {
    // Clean previous import temp dir
    const existingDir = await FileSystem.getInfoAsync(importTempDir);
    if (existingDir.exists) {
      await FileSystem.deleteAsync(importTempDir, { idempotent: true });
    }

    logDev('[Import] unzipping', { zipUri });

    // Unzip to temp directory
    const unzippedPath = await unzip(zipUri, importTempDir);
    logDev('[Import] unzipped', { unzippedPath });

    // Read manifest to validate
    const manifest = await readJsonSafe<Record<string, unknown>>(
      `${importTempDir}manifest.json`,
      {}
    );
    if (!manifest.exportVersion) {
      return { success: false, error: 'Invalid export file — missing manifest.' };
    }

    logDev('[Import] manifest validated', { manifest });

    // Ensure directories exist
    await ensureDir(gardenStorageDir);
    await ensureDir(photosDir);

    // Restore JSON data files
    const dataFiles: { src: string; dest: string }[] = [
      { src: 'plants.json', dest: `${gardenStorageDir}saved-plants.json` },
      { src: 'intelligence-profiles.json', dest: `${gardenStorageDir}plant-intelligence-profiles.json` },
      { src: 'investigations.json', dest: `${gardenStorageDir}investigations.json` },
      { src: 'app-settings.json', dest: `${gardenStorageDir}app-settings.json` },
      { src: 'wishlist-items.json', dest: `${gardenStorageDir}wishlist-items.json` },
      { src: 'care-task-actions.json', dest: `${gardenStorageDir}care-task-actions.json` },
      { src: 'notification-states.json', dest: `${gardenStorageDir}notification-states.json` },
    ];

    for (const { src, dest } of dataFiles) {
      const srcPath = `${importTempDir}${src}`;
      const info = await FileSystem.getInfoAsync(srcPath);
      if (info.exists) {
        // Read the export format and convert to storage format
        const data = await FileSystem.readAsStringAsync(srcPath);
        if (src === 'plants.json') {
          // Export stores as array, storage expects { savedPlants: [...] }
          const plants = JSON.parse(data);
          await FileSystem.writeAsStringAsync(dest, JSON.stringify({ savedPlants: plants }, null, 2));
        } else {
          await FileSystem.writeAsStringAsync(dest, data);
        }
        logDev('[Import] restored', { src, dest });
      }
    }

    // Restore infrastructure if present
    const infraSrc = `${importTempDir}infrastructure.json`;
    const infraInfo = await FileSystem.getInfoAsync(infraSrc);
    if (infraInfo.exists) {
      const infraData = await FileSystem.readAsStringAsync(infraSrc);
      await FileSystem.writeAsStringAsync(`${gardenStorageDir}garden-infrastructure.json`, infraData);
      logDev('[Import] restored infrastructure', {});
    }

    // Restore photo metadata
    const photoMetaSrc = `${importTempDir}photo-metadata.json`;
    const photoMetaInfo = await FileSystem.getInfoAsync(photoMetaSrc);
    let photoCount = 0;

    if (photoMetaInfo.exists) {
      const photoMetaRaw = await FileSystem.readAsStringAsync(photoMetaSrc);
      const photoMeta = JSON.parse(photoMetaRaw) as Record<string, Partial<PhotoRecord>>;

      // Copy photo files and fix URIs
      const restoredRecords: Record<string, PhotoRecord> = {};

      for (const [id, partial] of Object.entries(photoMeta)) {
        const record = { ...partial } as PhotoRecord;

        // Copy original photo
        if (partial.originalUri) {
          const srcPath = `${importTempDir}${partial.originalUri}`;
          const photoType = partial.photoType || 'memory';
          const destPath = `${photosDir}${photoType}/${partial.photoId}.jpg`;
          const copied = await copyFileIfExists(srcPath, destPath);
          if (copied) {
            record.originalUri = destPath;
            photoCount++;
          } else {
            record.originalUri = '';
          }
        }

        // Copy thumbnail
        if (partial.thumbnailUri) {
          const srcPath = `${importTempDir}${partial.thumbnailUri}`;
          const photoType = partial.photoType || 'memory';
          const destPath = `${photosDir}${photoType}/${partial.photoId}_thumb.jpg`;
          const copied = await copyFileIfExists(srcPath, destPath);
          if (copied) {
            record.thumbnailUri = destPath;
          }
        }

        // Copy compressed copy (if present) and remap its URI to the new device.
        // Without this, compressed photos come back with a stale absolute path
        // pointing at the old phone.
        if (partial.compressedUri) {
          const srcPath = `${importTempDir}${partial.compressedUri}`;
          const photoType = partial.photoType || 'memory';
          const destPath = `${photosDir}${photoType}/${partial.photoId}_compressed.jpg`;
          const copied = await copyFileIfExists(srcPath, destPath);
          if (copied) {
            record.compressedUri = destPath;
          }
        }

        // Set activeUri to compressed or original
        record.activeUri = record.compressedUri || record.originalUri || '';

        restoredRecords[id] = record;
      }

      await savePhotoRecords(restoredRecords);
      logDev('[Import] restored photo records', { count: Object.keys(restoredRecords).length });
    }

    // Clean up temp directory
    await FileSystem.deleteAsync(importTempDir, { idempotent: true });

    const plantCount = Array.isArray(JSON.parse(
      await FileSystem.readAsStringAsync(`${gardenStorageDir}saved-plants.json`).catch(() => '{"savedPlants":[]}')
    )?.savedPlants) ? JSON.parse(
      await FileSystem.readAsStringAsync(`${gardenStorageDir}saved-plants.json`).catch(() => '{"savedPlants":[]}')
    ).savedPlants.length : 0;

    return {
      success: true,
      plantCount: (manifest.plantCount as number) || plantCount,
      photoCount,
    };
  } catch (error) {
    const msg = error instanceof Error ? error.message : 'unknown error';
    warnDev('[Import] failed', { error: msg });
    // Clean up on failure
    await FileSystem.deleteAsync(importTempDir, { idempotent: true }).catch(() => {});
    return { success: false, error: msg };
  }
}
