import * as FileSystem from 'expo-file-system';

import type { AppSettings, ArtificialLightType, CareTaskActionState, ChildAgeRange, FieldAlbum, GardenContextPhoto, GardenInfrastructureState, HumidityProfile, LightProfile, OutdoorLightQuality, OutdoorSunTiming, PetAccessMode, PetType, PlantNotificationState, PlantSetupProfile, RoomProfile, SpaceType, WishlistItem, WishlistVisibility } from '../types/garden';
import type { PlantType, SavedPlantProfile } from '../types/plantScan';
import { resolvePlantSpaceId } from './gardenSelectors';

declare const __DEV__: boolean;

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

const gardenStorageDir = `${FileSystem.documentDirectory || ''}pixiesprout-garden/`;
const savedPlantsPath = `${gardenStorageDir}saved-plants.json`;
const wishlistItemsPath = `${gardenStorageDir}wishlist-items.json`;
const fieldAlbumsPath = `${gardenStorageDir}field-albums.json`;
const excursionStatePath = `${gardenStorageDir}excursion-state.json`;
const infrastructurePath = `${gardenStorageDir}garden-infrastructure.json`;
const careTaskActionsPath = `${gardenStorageDir}care-task-actions.json`;
const notificationStatesPath = `${gardenStorageDir}notification-states.json`;
const appSettingsPath = `${gardenStorageDir}app-settings.json`;
const fallbackSpaceType: SpaceType = 'living_room';
const spaceTypes: SpaceType[] = [
  'bedroom',
  'living_room',
  'kitchen',
  'bathroom',
  'office',
  'nursery',
  'balcony',
  'patio',
  'greenhouse',
  'grow_tent',
  'windowsill',
  'propagation_station',
  'seed_starting_area',
  'garden',
  'orchard',
];
const lightProfiles: LightProfile[] = ['low_light', 'medium_light', 'bright_indirect', 'direct_sun'];
const humidityProfiles: HumidityProfile[] = ['dry', 'average', 'humid'];
const outdoorSunTimings: OutdoorSunTiming[] = ['morning_sun', 'afternoon_sun', 'evening_sun', 'all_day_sun'];
const outdoorLightQualities: OutdoorLightQuality[] = ['direct_sun', 'filtered_light', 'partial_shade', 'full_shade', 'unsure'];
const artificialLightTypes: ArtificialLightType[] = ['led', 'grow_light', 'fluorescent', 'other'];
const careTaskActionStatuses = ['due', 'completed', 'snoozed', 'skipped'];
const petTypes: PetType[] = ['cat', 'dog', 'bird', 'rabbit', 'other'];
const childAgeRanges: ChildAgeRange[] = ['baby_toddler', 'young_child', 'older_child_teen'];
const wishlistVisibilities: WishlistVisibility[] = ['private', 'friends', 'community'];
const petAccessModes: PetAccessMode[] = ['whole_home', 'specific_spaces'];

export function createGardenId(prefix: string) {
  return `${prefix}_${Date.now()}_${Math.round(Math.random() * 10000)}`;
}

function normalizeContextPhotos(value: unknown, legacyUris: unknown[] = []): GardenContextPhoto[] {
  const now = new Date().toISOString();
  const rawItems = Array.isArray(value) ? value : [];
  const photos: GardenContextPhoto[] = [];
  const seenUris = new Set<string>();

  const addPhoto = (uri?: string, note?: string, createdAt?: string, id?: string) => {
    const cleanUri = uri?.trim();
    if (!cleanUri || seenUris.has(cleanUri) || photos.length >= 5) return;
    seenUris.add(cleanUri);
    photos.push({
      id: id || `photo_${Math.abs(cleanUri.split('').reduce((hash, char) => ((hash << 5) - hash) + char.charCodeAt(0), 0))}`,
      uri: cleanUri,
      note: note?.trim() || undefined,
      createdAt: createdAt || now,
    });
  };

  rawItems.forEach(item => {
    if (typeof item === 'string') {
      addPhoto(item);
      return;
    }
    if (item && typeof item === 'object') {
      const photo = item as Partial<GardenContextPhoto>;
      addPhoto(photo.uri, photo.note, photo.createdAt, photo.id);
    }
  });

  legacyUris.forEach(uri => {
    if (typeof uri === 'string') addPhoto(uri);
    if (Array.isArray(uri)) uri.forEach(item => typeof item === 'string' ? addPhoto(item) : undefined);
  });

  return photos;
}

function contextPhotoUris(photos?: GardenContextPhoto[]) {
  return photos?.map(photo => photo.uri).filter(Boolean) || [];
}

async function ensureGardenStorageDir() {
  if (!FileSystem.documentDirectory) return;
  const info = await FileSystem.getInfoAsync(gardenStorageDir);
  if (!info.exists) {
    await FileSystem.makeDirectoryAsync(gardenStorageDir, { intermediates: true });
  }
}

async function readJson<T>(path: string, fallback: T): Promise<T> {
  try {
    if (!FileSystem.documentDirectory) return fallback;
    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 writeJson<T>(path: string, value: T): Promise<void> {
  if (!FileSystem.documentDirectory) return;
  await ensureGardenStorageDir();
  // Atomic write (H8): write to a temp file then rename over the target. A
  // mid-write kill on a direct write corrupts the file and readJson silently
  // returns fallback {} / [], wiping data. Rename is atomic on Android.
  const tmpPath = `${path}.tmp`;
  await FileSystem.writeAsStringAsync(tmpPath, JSON.stringify(value));
  await FileSystem.moveAsync({ from: tmpPath, to: path });
}

export const emptyGardenInfrastructureState: GardenInfrastructureState = {
  rooms: [],
  roomOrderKeys: [],
  plantSetups: [],
  samplePlants: [],
  samplePlantsSeeded: false,
  gardenOrderKeys: [],
  sampleWatercolorIcons: {},
};

export const DEFAULT_AFTER_WORK_TIME = '5:30 PM';

export const defaultAppSettings: AppSettings = {
  reminderPreferences: {
    preferredReminderTime: null,
    afterWorkReminderTime: DEFAULT_AFTER_WORK_TIME,
  },
  householdSafety: {
    hasPets: false,
    petTypes: [],
    petAccess: 'whole_home',
    petAccessSpaceIds: [],
    hasChildren: false,
    childAgeRanges: [],
  },
  profileCommunity: {
    birthday: null,
    wishlistVisibility: 'private',
    communityGiftingPreferences: null,
  },
  privacy: {
    approximateLocationContext: null,
    photoPrivacy: 'device_only',
    progressPhotoHistoryEnabled: false,
    aiPersonalizationEnabled: false,
    gpsPrecision: 'rough',
    rememberGpsPrecision: false,
    gardenLocation: null,
  },
  carePreferences: {
    organicFirst: false,
    petSafeCaution: true,
    ediblePlantCaution: true,
    shoppingStyle: 'either',
  },
  gardenView: { plantFilter: 'all', plantSort: 'needs_care' },
  updatedAt: '2026-01-01T00:00:00.000Z',
};

export async function loadSavedPlantProfiles(): Promise<SavedPlantProfile[]> {
  const state = await readJson<{ savedPlants: SavedPlantProfile[] }>(savedPlantsPath, { savedPlants: [] });
  return Array.isArray(state.savedPlants)
    ? state.savedPlants.map(plant => normalizeSavedPlantProfile(plant))
    : [];
}

export async function saveSavedPlantProfiles(savedPlants: SavedPlantProfile[]): Promise<void> {
  await writeJson(savedPlantsPath, { savedPlants });
}

function normalizeWishlistItem(item: WishlistItem): WishlistItem | null {
  if (!item?.id || !item.imageUri || !item.commonName) return null;
  const wishlistVisibility = wishlistVisibilities.includes(item.wishlistVisibility as WishlistVisibility)
    ? item.wishlistVisibility
    : defaultAppSettings.profileCommunity.wishlistVisibility;

  // Preserve ALL fields (Bekky, 2026-08-25): the previous version rebuilt the
  // object with only a fixed subset, silently DROPPING the rich scan data
  // (description, propagation, taxonomy, sourceUrl/referenceUrl, petSafety,
  // kingdom/edibility/cultivable/substrate, commonNames, plantType, scanMode)
  // on every app-open reload. Spread first, then coerce the known scalars so
  // old/partial persisted items still normalize correctly.
  return {
    ...item,
    createdAt: item.createdAt || new Date().toISOString(),
    source: 'scan',
    confidence: typeof item.confidence === 'number' ? item.confidence : 0,
    wishlistVisibility,
  };
}

export async function loadWishlistItems(): Promise<WishlistItem[]> {
  const state = await readJson<{ wishlistItems: WishlistItem[] }>(wishlistItemsPath, { wishlistItems: [] });
  return Array.isArray(state.wishlistItems)
    ? state.wishlistItems.map(normalizeWishlistItem).filter((item): item is WishlistItem => Boolean(item))
    : [];
}

export async function saveWishlistItems(wishlistItems: WishlistItem[]): Promise<void> {
  await writeJson(wishlistItemsPath, { wishlistItems });
}

// ── Field Diary albums (Bekky, 2026-08-25) ──────────────────────────────
export async function loadFieldAlbums(): Promise<FieldAlbum[]> {
  const state = await readJson<{ fieldAlbums: FieldAlbum[] }>(fieldAlbumsPath, { fieldAlbums: [] });
  return Array.isArray(state.fieldAlbums) ? state.fieldAlbums : [];
}

export async function saveFieldAlbums(fieldAlbums: FieldAlbum[]): Promise<void> {
  await writeJson(fieldAlbumsPath, { fieldAlbums });
}

/** Load the persisted active excursion state (null if none). */
export async function loadExcursionState(): Promise<import('../types/garden').ExcursionState | null> {
  const state = await readJson<{ excursionState?: import('../types/garden').ExcursionState | null }>(excursionStatePath, { excursionState: null });
  return state.excursionState ?? null;
}

/** Persist the active excursion state (or null to clear it). */
export async function saveExcursionState(state: import('../types/garden').ExcursionState | null): Promise<void> {
  await writeJson(excursionStatePath, { excursionState: state });
}

function normalizeCareTaskActionState(action: CareTaskActionState): CareTaskActionState | null {
  if (!action?.taskId || !action.plantId || !action.taskType || !careTaskActionStatuses.includes(action.status)) return null;
  return {
    taskId: action.taskId,
    plantId: action.plantId,
    spaceId: action.spaceId,
    taskType: action.taskType,
    dueDate: action.dueDate,
    source: action.source,
    metadata: action.metadata,
    notificationQuickActions: action.notificationQuickActions,
    reminderPreferences: action.reminderPreferences,
    status: action.status,
    completedAt: action.completedAt,
    snoozedUntil: action.snoozedUntil,
    skippedAt: action.skippedAt,
    updatedAt: action.updatedAt || new Date().toISOString(),
  };
}

export async function loadCareTaskActionStates(): Promise<Record<string, CareTaskActionState>> {
  const state = await readJson<{ careTaskActions: Record<string, CareTaskActionState> }>(careTaskActionsPath, { careTaskActions: {} });
  return Object.entries(state.careTaskActions || {}).reduce<Record<string, CareTaskActionState>>((acc, [taskId, action]) => {
    const normalized = normalizeCareTaskActionState({ ...action, taskId: action.taskId || taskId });
    if (normalized) acc[normalized.taskId] = normalized;
    return acc;
  }, {});
}

export async function saveCareTaskActionStates(careTaskActions: Record<string, CareTaskActionState>): Promise<void> {
  await writeJson(careTaskActionsPath, { careTaskActions });
}

export async function resetCareTaskActionStates(): Promise<void> {
  await saveCareTaskActionStates({});
}

function normalizePlantNotificationState(state: PlantNotificationState): PlantNotificationState | null {
  if (!state?.id) return null;
  return {
    id: state.id,
    isRead: Boolean(state.isRead),
    isDismissed: Boolean(state.isDismissed),
    remindedAt: state.remindedAt,
    snoozedUntil: state.snoozedUntil,
    updatedAt: state.updatedAt || new Date().toISOString(),
  };
}

export async function loadPlantNotificationStates(): Promise<Record<string, PlantNotificationState>> {
  const state = await readJson<{ notificationStates: Record<string, PlantNotificationState> }>(notificationStatesPath, { notificationStates: {} });
  return Object.entries(state.notificationStates || {}).reduce<Record<string, PlantNotificationState>>((acc, [id, notificationState]) => {
    const normalized = normalizePlantNotificationState({ ...notificationState, id: notificationState.id || id });
    if (normalized) acc[normalized.id] = normalized;
    return acc;
  }, {});
}

export async function savePlantNotificationStates(notificationStates: Record<string, PlantNotificationState>): Promise<void> {
  await writeJson(notificationStatesPath, { notificationStates });
}

function filterKnownValues<T extends string>(value: unknown, allowed: T[]): T[] {
  return Array.isArray(value) ? value.filter((item): item is T => allowed.includes(item as T)) : [];
}

function normalizeAppSettings(settings: Partial<AppSettings>): AppSettings {
  const householdSafety = settings.householdSafety || defaultAppSettings.householdSafety;
  const profileCommunity = settings.profileCommunity || defaultAppSettings.profileCommunity;
  const privacy = settings.privacy || defaultAppSettings.privacy;
  const carePreferences = settings.carePreferences || defaultAppSettings.carePreferences;
  const wishlistVisibility = wishlistVisibilities.includes(profileCommunity.wishlistVisibility as WishlistVisibility)
    ? profileCommunity.wishlistVisibility
    : defaultAppSettings.profileCommunity.wishlistVisibility;
  const petAccess = petAccessModes.includes(householdSafety.petAccess as PetAccessMode)
    ? householdSafety.petAccess
    : defaultAppSettings.householdSafety.petAccess;

  return {
    reminderPreferences: {
      ...defaultAppSettings.reminderPreferences,
      ...(settings.reminderPreferences || {}),
      afterWorkReminderTime: settings.reminderPreferences?.afterWorkReminderTime || defaultAppSettings.reminderPreferences.afterWorkReminderTime,
    },
    householdSafety: {
      hasPets: Boolean(householdSafety.hasPets),
      petTypes: filterKnownValues(householdSafety.petTypes, petTypes),
      petAccess,
      petAccessSpaceIds: normalizeStringArray(householdSafety.petAccessSpaceIds),
      hasChildren: Boolean(householdSafety.hasChildren),
      childAgeRanges: filterKnownValues(householdSafety.childAgeRanges, childAgeRanges),
    },
    profileCommunity: {
      birthday: profileCommunity.birthday || null,
      wishlistVisibility,
      communityGiftingPreferences: profileCommunity.communityGiftingPreferences || null,
    },
    privacy: {
      ...defaultAppSettings.privacy,
      ...privacy,
    },
    carePreferences: {
      ...defaultAppSettings.carePreferences,
      ...carePreferences,
    },
    gardenView: settings.gardenView || defaultAppSettings.gardenView,
    updatedAt: settings.updatedAt || new Date().toISOString(),
  };
}

export async function loadAppSettings(): Promise<AppSettings> {
  const state = await readJson<{ appSettings: Partial<AppSettings> }>(appSettingsPath, { appSettings: defaultAppSettings });
  return normalizeAppSettings(state.appSettings || defaultAppSettings);
}

export async function saveAppSettings(appSettings: AppSettings): Promise<void> {
  await writeJson(appSettingsPath, { appSettings: normalizeAppSettings({ ...appSettings, updatedAt: new Date().toISOString() }) });
}

export async function loadGardenInfrastructure(): Promise<GardenInfrastructureState> {
  const state = await readJson<GardenInfrastructureState>(infrastructurePath, emptyGardenInfrastructureState);
  const loadedState = {
    ...emptyGardenInfrastructureState,
    ...state,
    rooms: (state.rooms || []).map(room => normalizeRoomProfile(room)),
    plantSetups: (state.plantSetups || []).map(setup => normalizePlantSetupProfile(setup)),
    samplePlants: state.samplePlants || [],
    sampleWatercolorIcons: state.sampleWatercolorIcons || {},
  };
  if (isDevRuntime()) console.log('LOADED_GARDEN_STATE_FROM_STORAGE', loadedState);
  return loadedState;
}

export async function saveGardenInfrastructure(state: GardenInfrastructureState): Promise<void> {
  if (isDevRuntime()) console.log('WRITING_GARDEN_STATE_TO_STORAGE', state);
  await writeJson(infrastructurePath, state);
}

export function normalizeStringArray(value: unknown): string[] {
  if (Array.isArray(value)) return value.map(item => String(item).trim()).filter(Boolean);
  if (typeof value === 'string' && value.trim()) return [value.trim()];
  return [];
}

function normalizeSavedPlantIdentification(plant: SavedPlantProfile): SavedPlantProfile['identification'] {
  const identification = plant.identification;
  if (!identification && !plant.scanResultId) return undefined;

  const suggestions = Array.isArray(identification?.suggestions)
    ? identification.suggestions
        .map(suggestion => ({
          name: String(suggestion.name || suggestion.commonName || suggestion.scientificName || '').trim(),
          commonName: suggestion.commonName,
          scientificName: suggestion.scientificName,
          confidence: typeof suggestion.confidence === 'number' ? suggestion.confidence : undefined,
        }))
        .filter(suggestion => suggestion.name)
    : plant.alternatives.map(name => ({ name }));

  return {
    scanResultId: identification?.scanResultId || plant.scanResultId,
    provider: identification?.provider || plant.scanProvider,
    providerRequestId: identification?.providerRequestId || plant.scanProviderRequestId,
    identifiedAt: identification?.identifiedAt || plant.createdAt,
    imageUri: identification?.imageUri || plant.photoUri,
    imageUris: normalizeStringArray(identification?.imageUris),
    commonName: identification?.commonName || plant.commonName,
    scientificName: identification?.scientificName || plant.scientificName,
    confidence: typeof identification?.confidence === 'number' ? identification.confidence : plant.confidence,
    suggestions,
    description: identification?.description,
    sourceUrl: identification?.sourceUrl || plant.scanSourceUrl,
    referenceUrl: identification?.referenceUrl,
    referenceTitle: identification?.referenceTitle,
    referenceSource: identification?.referenceSource,
    notes: identification?.notes || plant.notes,
  };
}

export function normalizeSavedPlantProfile(plant: SavedPlantProfile): SavedPlantProfile {
  return {
    ...plant,
    name: plant.name || plant.commonName,
    category: plant.category || plant.plantType,
    plantingMedium: normalizeStringArray(plant.plantingMedium),
    treatmentPreferences: plant.treatmentPreferences || [],
    careGoals: plant.careGoals || [],
    commonIssues: plant.commonIssues || [],
    manualNotes: plant.manualNotes || plant.notes,
    setupCompleted: Boolean(plant.setupCompleted),
    identification: normalizeSavedPlantIdentification(plant),
  };
}

function normalizeGardenName(value?: string) {
  return (value || '').trim().toLowerCase().replace(/[_-]+/g, ' ').replace(/\s+/g, ' ');
}

function isSpaceType(value: unknown): value is SpaceType {
  return typeof value === 'string' && spaceTypes.includes(value as SpaceType);
}

function isLightProfile(value: unknown): value is LightProfile {
  return typeof value === 'string' && lightProfiles.includes(value as LightProfile);
}

function isHumidityProfile(value: unknown): value is HumidityProfile {
  return typeof value === 'string' && humidityProfiles.includes(value as HumidityProfile);
}

function normalizeOutdoorSunTimings(value: unknown): OutdoorSunTiming[] {
  const timings = Array.isArray(value) ? value.filter((item): item is OutdoorSunTiming => outdoorSunTimings.includes(item as OutdoorSunTiming)) : [];
  return timings.includes('all_day_sun') ? ['all_day_sun'] : timings;
}

function normalizeOutdoorLightQuality(value: unknown): OutdoorLightQuality {
  return outdoorLightQualities.includes(value as OutdoorLightQuality) ? value as OutdoorLightQuality : 'unsure';
}

function normalizeArtificialLightTypes(value: unknown): ArtificialLightType[] {
  return Array.isArray(value) ? value.filter((item): item is ArtificialLightType => artificialLightTypes.includes(item as ArtificialLightType)) : [];
}

function normalizeArtificialLightHours(value: unknown): number | undefined {
  if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) return undefined;
  return Math.min(24, Math.round(value * 10) / 10);
}

export function inferSpaceType(input: { name?: string; type?: string; spaceType?: string }): SpaceType {
  if (isSpaceType(input.spaceType)) return input.spaceType;
  if (input.spaceType === 'garden_bed' || input.spaceType === 'raised_bed') return 'garden';
  if (input.spaceType === 'plant_shelf') return 'living_room';

  const haystack = `${normalizeGardenName(input.name)} ${normalizeGardenName(input.type)}`;
  const matches: { pattern: string; spaceType: SpaceType }[] = [
    { pattern: 'living room', spaceType: 'living_room' },
    { pattern: 'bedroom', spaceType: 'bedroom' },
    { pattern: 'kitchen', spaceType: 'kitchen' },
    { pattern: 'bathroom', spaceType: 'bathroom' },
    { pattern: 'office', spaceType: 'office' },
    { pattern: 'nursery', spaceType: 'nursery' },
    { pattern: 'balcony', spaceType: 'balcony' },
    { pattern: 'patio', spaceType: 'patio' },
    { pattern: 'porch', spaceType: 'patio' },
    { pattern: 'greenhouse', spaceType: 'greenhouse' },
    { pattern: 'grow tent', spaceType: 'grow_tent' },
    { pattern: 'windowsill', spaceType: 'windowsill' },
    { pattern: 'plant shelf', spaceType: 'living_room' },
    { pattern: 'propagation', spaceType: 'propagation_station' },
    { pattern: 'seed starting', spaceType: 'seed_starting_area' },
    { pattern: 'garden bed', spaceType: 'garden' },
    { pattern: 'raised bed', spaceType: 'garden' },
    { pattern: 'orchard', spaceType: 'orchard' },
    { pattern: 'yard', spaceType: 'garden' },
    { pattern: 'garden', spaceType: 'garden' },
  ];
  return matches.find(match => haystack.includes(match.pattern))?.spaceType || fallbackSpaceType;
}

export function inferLightProfile(input: { lightProfile?: string; lightLevel?: string }): LightProfile {
  if (isLightProfile(input.lightProfile)) return input.lightProfile;
  if (input.lightLevel === 'low') return 'low_light';
  if (input.lightLevel === 'medium') return 'medium_light';
  if (input.lightLevel === 'bright_indirect' || input.lightLevel === 'grow_light') return 'bright_indirect';
  if (input.lightLevel === 'direct_sun') return 'direct_sun';
  return 'medium_light';
}

export function inferHumidityProfile(input: { humidityProfile?: string; humidityEstimate?: string }): HumidityProfile {
  if (isHumidityProfile(input.humidityProfile)) return input.humidityProfile;
  if (input.humidityEstimate === 'dry') return 'dry';
  if (input.humidityEstimate === 'humid') return 'humid';
  return 'average';
}

// WINDOW DIRECTIONS MIGRATION (Bekky, 2026-09-08): windowDirection was a single
// value ('north' | ... | 'multiple' | 'none' | 'unsure'). It's now an ARRAY
// (windowDirections) so a space can have East + South windows as two separate
// selections instead of a forced "Southeast". This normalizes legacy stored
// values: a single string → a one-element array; 'multiple' → the cardinal
// directions (best-effort, since the old value didn't say which); 'none'/'unsure'
// → their own single-element array. Unknown/empty → [].
const WINDOW_DIRECTION_VALUES = new Set(['north', 'south', 'east', 'west', 'northeast', 'northwest', 'southeast', 'southwest', 'none', 'unsure']);
export function normalizeWindowDirections(input: { windowDirections?: unknown; windowDirection?: unknown }): RoomProfile['windowDirections'] {
  const raw = input.windowDirections ?? input.windowDirection;
  if (Array.isArray(raw)) {
    return raw.filter((d): d is RoomProfile['windowDirections'][number] => typeof d === 'string' && WINDOW_DIRECTION_VALUES.has(d));
  }
  if (typeof raw === 'string') {
    if (raw === 'multiple') return ['north', 'south', 'east', 'west'];
    if (WINDOW_DIRECTION_VALUES.has(raw)) return [raw as RoomProfile['windowDirections'][number]];
    return [];
  }
  return [];
}

export function normalizeRoomProfile(room: RoomProfile): RoomProfile {
  const cleanedNotes = room.notes
    ?.replace(/\s*Former space type: (Garden Bed|Raised Bed|Plant Shelf)\./g, '')
    .trim();
  const environmentPhotos = normalizeContextPhotos(room.environmentPhotos, [room.environmentPhotoUri, room.photoUri]);
  const environmentPhotoUri = environmentPhotos[0]?.uri || room.environmentPhotoUri || room.photoUri;
  return {
    ...room,
    spaceType: inferSpaceType(room),
    lightProfile: inferLightProfile(room),
    humidityProfile: inferHumidityProfile(room),
    windowDirections: normalizeWindowDirections(room),
    outdoorSunTimings: normalizeOutdoorSunTimings(room.outdoorSunTimings),
    outdoorLightQuality: normalizeOutdoorLightQuality(room.outdoorLightQuality),
    usesAmbientArtificialLight: Boolean(room.usesAmbientArtificialLight || (room as RoomProfile & { usesArtificialLight?: boolean }).usesArtificialLight),
    ambientArtificialLightTypes: normalizeArtificialLightTypes(room.ambientArtificialLightTypes || (room as RoomProfile & { artificialLightTypes?: ArtificialLightType[] }).artificialLightTypes),
    ambientArtificialLightHoursPerDay: normalizeArtificialLightHours(room.ambientArtificialLightHoursPerDay ?? (room as RoomProfile & { artificialLightHoursPerDay?: number }).artificialLightHoursPerDay),
    photoUri: environmentPhotoUri,
    environmentPhotoUri,
    environmentPhotos,
    notes: cleanedNotes || undefined,
  };
}

export function migratePlantSpaceLinks(plants: SavedPlantProfile[], spaces: RoomProfile[]) {
  let changed = false;
  const migratedPlants = plants.map(plant => {
    const normalized = normalizeSavedPlantProfile(plant);
    const resolvedSpaceId = resolvePlantSpaceId(normalized, spaces);
    const validSpaceId = normalized.spaceId && spaces.some(space => space.id === normalized.spaceId) ? normalized.spaceId : undefined;
    const nextSpaceId = validSpaceId || resolvedSpaceId || null;
    const shouldClearLegacy = normalized.assignedSpaceId !== (nextSpaceId || undefined) || Boolean(normalized.spaceName || normalized.space || normalized.location);
    const shouldUpdateSpace = normalized.spaceId !== nextSpaceId;

    if (!shouldClearLegacy && !shouldUpdateSpace) return normalized;

    changed = true;
    return {
      ...normalized,
      spaceId: nextSpaceId,
      assignedSpaceId: nextSpaceId || undefined,
      spaceName: undefined,
      space: undefined,
      location: undefined,
      locationContext: nextSpaceId ? 'home' : 'unsure',
      updatedAt: new Date().toISOString(),
    };
  });

  return { plants: migratedPlants, changed };
}

export function createManualPlantProfile(input: {
  name: string;
  scientificName?: string;
  plantType: PlantType;
  photoUri?: string;
  spaceId?: string | null;
  growthStage?: SavedPlantProfile['growthStage'];
  timeOwned?: SavedPlantProfile['timeOwned'];
  notes?: string;
  manualNotes?: string;
}): SavedPlantProfile {
  const now = new Date().toISOString();
  const scientificName = input.scientificName?.trim() || input.name;
  const notes = input.manualNotes || input.notes;

  return {
    id: createGardenId('plant'),
    photoUri: input.photoUri || '',
    image: input.photoUri,
    name: input.name,
    commonName: input.name,
    scientificName,
    confidence: 100,
    alternatives: [],
    category: input.plantType,
    plantType: input.plantType,
    growthStage: input.growthStage || 'unsure',
    timeOwned: input.timeOwned || 'unsure',
    locationContext: input.spaceId ? 'home' : 'unsure',
    spaceId: input.spaceId,
    assignedSpaceId: input.spaceId || undefined,
    careDifficulty: 'unsure',
    plantingMedium: [],
    treatmentPreferences: [],
    isEdible: 'unsure',
    petChildCaution: 'unsure',
    careGoals: [],
    commonIssues: [],
    tags: [],
    notes,
    manualNotes: notes,
    setupCompleted: false,
    careSummary: 'No care plan generated yet.',
    toxicityWarning: 'Safety and toxicity have not been checked yet.',
    createdAt: now,
    updatedAt: now,
    source: 'manual_garden',
  };
}

export function updateSavedPlantProfile(
  profile: SavedPlantProfile,
  patch: Partial<Omit<SavedPlantProfile, 'id' | 'createdAt'>>,
): SavedPlantProfile {
  return { ...profile, ...patch, updatedAt: new Date().toISOString() };
}

export function createRoomProfile(input: Omit<RoomProfile, 'id' | 'createdAt' | 'updatedAt'>): RoomProfile {
  const now = new Date().toISOString();
  const environmentPhotos = normalizeContextPhotos(input.environmentPhotos, [input.environmentPhotoUri, input.photoUri]);
  const environmentPhotoUri = environmentPhotos[0]?.uri || input.environmentPhotoUri || input.photoUri;
  return {
    ...input,
    spaceType: inferSpaceType(input),
    lightProfile: inferLightProfile(input),
    humidityProfile: inferHumidityProfile(input),
    windowDirections: normalizeWindowDirections(input),
    outdoorSunTimings: normalizeOutdoorSunTimings(input.outdoorSunTimings),
    outdoorLightQuality: normalizeOutdoorLightQuality(input.outdoorLightQuality),
    usesAmbientArtificialLight: Boolean(input.usesAmbientArtificialLight),
    ambientArtificialLightTypes: normalizeArtificialLightTypes(input.ambientArtificialLightTypes),
    ambientArtificialLightHoursPerDay: normalizeArtificialLightHours(input.ambientArtificialLightHoursPerDay),
    photoUri: environmentPhotoUri,
    environmentPhotoUri,
    environmentPhotos,
    id: createGardenId('room'),
    createdAt: now,
    updatedAt: now,
  };
}

export function updateRoomProfile(profile: RoomProfile, patch: Partial<Omit<RoomProfile, 'id' | 'createdAt'>>): RoomProfile {
  const next = { ...profile, ...patch };
  const environmentPhotos = normalizeContextPhotos(next.environmentPhotos, [next.environmentPhotoUri, next.photoUri]);
  const environmentPhotoUri = environmentPhotos[0]?.uri || next.environmentPhotoUri || next.photoUri;
  return {
    ...next,
    spaceType: inferSpaceType(next),
    lightProfile: inferLightProfile(next),
    humidityProfile: inferHumidityProfile(next),
    windowDirections: normalizeWindowDirections(next),
    outdoorSunTimings: normalizeOutdoorSunTimings(next.outdoorSunTimings),
    outdoorLightQuality: normalizeOutdoorLightQuality(next.outdoorLightQuality),
    usesAmbientArtificialLight: Boolean(next.usesAmbientArtificialLight),
    ambientArtificialLightTypes: normalizeArtificialLightTypes(next.ambientArtificialLightTypes),
    ambientArtificialLightHoursPerDay: normalizeArtificialLightHours(next.ambientArtificialLightHoursPerDay),
    photoUri: environmentPhotoUri,
    environmentPhotoUri,
    environmentPhotos,
    updatedAt: new Date().toISOString(),
  };
}

export function normalizePlantSetupProfile(profile: PlantSetupProfile): PlantSetupProfile {
  const setupPhotos = normalizeContextPhotos(profile.setupPhotos, [profile.setupPhotoUri, profile.photoUris]);
  const photoUris = contextPhotoUris(setupPhotos);
  return {
    ...profile,
    usesDedicatedArtificialLight: Boolean(profile.usesDedicatedArtificialLight),
    dedicatedArtificialLightTypes: normalizeArtificialLightTypes(profile.dedicatedArtificialLightTypes),
    dedicatedArtificialLightHoursPerDay: normalizeArtificialLightHours(profile.dedicatedArtificialLightHoursPerDay),
    dedicatedArtificialLightDistance: profile.dedicatedArtificialLightDistance?.trim() || undefined,
    setupPhotoUri: setupPhotos[0]?.uri,
    setupPhotos,
    photoUris,
  };
}

export function createPlantSetupProfile(input: Omit<PlantSetupProfile, 'id' | 'createdAt' | 'updatedAt'>): PlantSetupProfile {
  const now = new Date().toISOString();
  return normalizePlantSetupProfile({ ...input, id: createGardenId('setup'), createdAt: now, updatedAt: now });
}

export function updatePlantSetupProfile(
  profile: PlantSetupProfile,
  patch: Partial<Omit<PlantSetupProfile, 'id' | 'createdAt'>>,
): PlantSetupProfile {
  return normalizePlantSetupProfile({ ...profile, ...patch, updatedAt: new Date().toISOString() });
}

// ─── Standalone Scan History ────────────────────────────────────────────────

export type StandaloneScanEntry = {
  id: string;
  savedAt: string;
  imageUri: string;
  symptoms: string[];
  userNote?: string;
  aiResult?: {
    summary: string;
    possibleCauses: string[];
    noticed: string[];
    suggestedChecks: string[];
    confidence: 'low' | 'medium' | 'high';
    uncertaintyNote?: string;
  };
};

const standaloneScansPath = `${gardenStorageDir}standalone-scans.json`;

export async function loadStandaloneScans(): Promise<StandaloneScanEntry[]> {
  try {
    const info = await FileSystem.getInfoAsync(standaloneScansPath);
    if (!info.exists) return [];
    const raw = await FileSystem.readAsStringAsync(standaloneScansPath);
    return JSON.parse(raw);
  } catch {
    return [];
  }
}

export async function saveStandaloneScan(entry: StandaloneScanEntry): Promise<void> {
  const existing = await loadStandaloneScans();
  const updated = [entry, ...existing];
  await FileSystem.writeAsStringAsync(standaloneScansPath, JSON.stringify(updated, null, 2));
}
