import type {
  PlantIdentificationResult,
  PlantSuggestionTaxonomy,
  SeedIdentificationResult,
} from '../services/plantIdentification/types';
import type { Cohort, GroupableCareAction } from './care';
import type {
  PlantCareDifficulty,
  PlantType,
  SavedPlantProfile,
  ScanMode,
  SeedKind,
  TreatmentPreference,
  YesNoUnsure,
} from './plantScan';
import type { SavedPropagationInfo } from './propagation';

/** How often a climate device runs (Bekky, 2026-08-23). 'never' = not used/absent. */
export type ClimateDeviceFrequency = 'never' | 'occasionally' | 'all_the_time';

/**
 * FELT ENGAGEMENT (Bekky, 2026-08-23): at what point the user *reaches for* a
 * serious climate device (A/C, heater). We deliberately do NOT ask the °C dial
 * setting — a "turbo + 16" A/C is desperation, not comfort, and the same person
 * reaches at different temps in different cities (comfortable without A/C in
 * mild DaLat, turbo+16 in hot Phuket). So we ask how the user FEELS it, then
 * derive the engagement °C from the garden's local climate — never ask the °C.
 * 'never' = don't use this device.
 */
export type FeltEngagement =
  | 'never'
  | 'only_extreme'      // only in genuinely extreme heat/cold (very tolerant)
  | 'when_hot_or_cold'  // reaches when it's clearly hot/cold (default)
  | 'when_bit_warm'     // reaches at the slightest warmth/cold (sensitive)
  | 'basically_always'; // runs most of the time when its season is on

/** How a heater dries the air, which scales how much it boosts watering (Bekky). */
export type HeatType =
  | 'radiant'            // baseboard / oil radiator / heated floor — mild
  | 'forced_air'        // furnace / heat pump — strong
  | 'woodstove_fireplace'; // intense + intermittent when actually burning


export type SpaceType =
  | 'bedroom'
  | 'living_room'
  | 'kitchen'
  | 'bathroom'
  | 'office'
  | 'nursery'
  | 'balcony'
  | 'patio'
  | 'greenhouse'
  | 'grow_tent'
  | 'windowsill'
  | 'propagation_station'
  | 'seed_starting_area'
  | 'garden'
  | 'orchard';

/** Persisted Garden-list filter and sort values. */
export type GardenPlantFilter = 'all' | 'needs_care' | 'missing_setup' | 'open_cases' | 'seeds';
export type GardenPlantSort = 'by_space' | 'recently_added' | 'name_az' | 'needs_care' | 'last_watered';

export type LightProfile = 'low_light' | 'medium_light' | 'bright_indirect' | 'direct_sun';

export type HumidityProfile = 'dry' | 'average' | 'humid';

export type OutdoorSunTiming = 'morning_sun' | 'afternoon_sun' | 'evening_sun' | 'all_day_sun';

export type OutdoorLightQuality = 'direct_sun' | 'filtered_light' | 'partial_shade' | 'full_shade' | 'unsure';

export type ArtificialLightType = 'led' | 'grow_light' | 'fluorescent' | 'other';

export type GardenContextPhoto = {
  id: string;
  uri: string;
  note?: string;
  createdAt: string;
  /**
   * How the photo was acquired. 'camera' = captured live in-app (heading/altitude
   * are captured at capture-time and are meaningful). 'gallery' = imported from
   * the photo library (heading/altitude are NOT captured — data would be stale).
   */
  source?: 'camera' | 'gallery';
  /** Compass heading in degrees (0-360) at capture time — camera photos only. */
  heading?: number;
  /** Altitude in meters at capture time — camera photos only, best-effort. */
  altitude?: number;
};

export type GardenPlant = {
  id: string;
  name: string;
  type: PlantType;
  imageUri?: string;
  avatarUri?: string;
  spaceId?: string | null;
  careDifficulty: PlantCareDifficulty;
  wateringSchedule?: string;
  lightNeeds?: string;
  plantingMedium?: string[];
  treatmentPreferences?: TreatmentPreference[];
  isEdible?: YesNoUnsure;
  petChildCaution?: YesNoUnsure;
  careGoals?: string[];
  commonIssues?: string[];
  tags: string[];
  notes?: string;
  createdAt: string;
  updatedAt: string;
};

export type WindowDirection = 'north' | 'south' | 'east' | 'west' | 'northeast' | 'northwest' | 'southeast' | 'southwest' | 'none' | 'unsure';

export type RoomProfile = {
  id: string;
  name: string;
  /** @deprecated Legacy room category; use spaceType for new code. */
  type: 'living_room' | 'bedroom' | 'bathroom' | 'kitchen' | 'office' | 'balcony' | 'porch' | 'yard' | 'greenhouse' | 'custom';
  spaceType: SpaceType;
  lightProfile: LightProfile;
  humidityProfile: HumidityProfile;
  windowDirections: WindowDirection[];
  lightLevel: 'low' | 'medium' | 'bright_indirect' | 'direct_sun' | 'grow_light' | 'unsure';
  outdoorSunTimings?: OutdoorSunTiming[];
  outdoorLightQuality?: OutdoorLightQuality;
  usesAmbientArtificialLight: boolean;
  ambientArtificialLightTypes: ArtificialLightType[];
  ambientArtificialLightHoursPerDay?: number;
  humidityEstimate: 'dry' | 'normal' | 'humid' | 'unsure';
  temperatureEstimate: 'cool' | 'normal' | 'warm' | 'fluctuates' | 'unsure';
  /**
   * CLIMATE & AIR (Bekky, 2026-08-21): which climate devices are present in an
   * INDOOR space, and whether it has a window the user opens by season. These
   * fold into the care cadence (seasonModifier) + the AI care-guidance packet.
   * Absent on outdoor spaces / older rooms.
   *
   * 2026-08-23: each device is now a FREQUENCY (never/occasionally/all_the_time)
   * instead of a boolean, so the cadence scales by how often it actually runs
   * (Bekky: "how often do you use it?"). 'never' = device not used/absent.
   */
  climateDevices?: {
    /** Air con. reach = when the user reaches for it (felt engagement); a
     *  threshold °C derived from the local climate decides WHEN it runs, so no
     *  separate frequency chip (an A/C only ever runs hot). 'never' = don't have. */
    airCon: { reach: FeltEngagement };
    /** Heater. reach = when; heatType = how strongly it dries. Same no-freq rule. */
    heater: { reach: FeltEngagement; heatType: HeatType };
    /** Fan / humidifier: plain frequency (they can run on mild days, so they keep
     *  their own "how often" — unlike A/C/heater where reach encodes it). */
    fan: ClimateDeviceFrequency;
    humidifier: ClimateDeviceFrequency;
  };
  /** How often the user opens the window (only when windowDirections doesn't include 'none').
   *  Simpler than per-season toggles (Bekky, 2026-08-21): 'all_the_time' |
   *  'occasionally' | 'seldom'. Window presence is derived from windowDirections
   *  (includes 'none' = no window). */
  windowOpenFrequency?: 'all_the_time' | 'occasionally' | 'seldom';
  /**
   * ELEVATION (Bekky, 2026-08-23): the building floor level this space sits on.
   * Applies to ALL spaces (indoor + outdoor). Higher floors get more wind, more
   * sun, less ground humidity; ground level is more sheltered but more humid +
   * more ground pests. 'ground' = ground level, 'floor_1' = first floor above
   * ground, 'floor_2_3' / 'floor_4_plus' band the higher floors. 'unsure' =
   * the user didn't answer (default, treated as neutral).
   */
  elevation?: 'ground' | 'floor_1' | 'floor_2_3' | 'floor_4_plus' | 'unsure';
  /**
   * OUTDOOR EXPOSURE (Bekky, 2026-08-23): how the space is covered/sheltered,
   * which changes how weather reaches its plants. Only relevant for outdoor
   * spaces (balcony/patio/garden/orchard); absent on indoor spaces.
   * roofType: what's overhead (blocks rain/sun/wind differently).
   * walls: how many sides are closed.
   * openFaces: which directions the open sides face (sideways-rain exposure).
   * These fold into the weather cadence modifier + the AI care packet.
   */
  exposure?: {
    roofType?: 'none' | 'solid' | 'glass' | 'permeable' | 'unsure';
    /** For glass roofs (Bekky, 2026-08-23): tinted/frosted glass blocks more UV +
     *  cuts heat build vs clear glass. Only relevant when roofType === 'glass'. */
    glassTint?: 'clear' | 'tinted' | 'frosted' | 'unsure';
    walls?: 'none' | 'one' | 'two' | 'three' | 'unsure';
    /** MULTI-SELECT (Bekky, 2026-08-23): which directions the OPEN sides face.
     *  Tap as many as apply (e.g. north + east). Only shown when there's a
     *  roof — an open-sky space has no meaningful enclosure. No 'mixed' —
     *  tap the actual directions. */
    openFaces?: ('north' | 'south' | 'east' | 'west')[];
  };
  notes?: string;
  photoUri?: string;
  environmentPhotoUri?: string;
  environmentPhotos?: GardenContextPhoto[];
  linkedPlantIds?: string[];
  linkedSpaceIds?: string[];
  /**
   * INTELLIGENT GROUPING COHORTS (Bekky, 2026-08-27, Chunk 2): per-action cohort
   * groups of this space's plants that share a cadence so the user can care for
   * a whole section in one sweeping action. Keyed by GroupableCareAction
   * ('water' | 'fertilize') — a water cohort is independent of a fertilize
   * cohort. Derived by the AI from the space's gathered per-plant care packages,
   * STICKY by default (not re-derived every refresh), re-grouped on major
   * guidance-changing events (repot, membership change, setup change). Additive
   * — absent on spaces never grouped.
   */
  cohorts?: Partial<Record<GroupableCareAction, Cohort[]>>;
  /**
   * SPACE DETAIL COLLAPSED TILES (Bekky, 2026-08-30): which space-detail cards
   * ("details" = Space Details, "plants" = Assigned Plants) are collapsed.
   * Persists the user's chevron preferences across app open/close. Absent =
   * all expanded.
   */
  collapsedTiles?: string[];
  /**
   * COHORT ROW EXPANSION (Bekky, 2026-08-30): which cohort rows in this
   * space's Care Groups card are expanded — persisted across app open/close.
   * Keys are `${cohort.id}:${action}`. Absent = all collapsed.
   */
  expandedCohortKeys?: string[];
  /**
   * INTELLIGENT GROUPINGS TOGGLE (Bekky, 2026-08-30, chunk B-adjacent): the
   * per-space user override. true (default) = AI cohort grouping runs for this
   * space. false = an escape hatch — the user just waters the whole space in
   * one go; cohort generation SKIPS the space, the Care tab renders its tasks
   * per-plant, and existing cohorts go DORMANT (kept on the room for
   * reversibility — flipping back ON restores them, no re-run from scratch).
   * Water-all is deliberately toggle-INDEPENDENT.
   */
  cohortsEnabled?: boolean;
  createdAt: string;
  updatedAt: string;
};

export type PlantSetupProfile = {
  id: string;
  plantId: string;
  roomId?: string;
  /** @deprecated Window distance moved to placement (Bekky, 2026-08-22) —
   *  now `SavedPlantProfile.placementProximity.window`. Kept optional only so
   *  legacy saved data still decodes. New setups no longer write it. */
  distanceFromWindow?: 'windowsill' | 'one_to_three_ft' | 'four_to_six_ft' | 'six_plus_ft' | 'not_applicable' | 'unsure';
  potType?: 'plastic' | 'ceramic' | 'terracotta' | 'nursery_pot' | 'self_watering' | 'hanging' | 'raised_bed' | 'garden_bed' | 'outdoor_container' | 'ground' | 'other' | 'unsure' | 'grow_bag' | 'window_box' | 'shared_planter' | 'custom';
  /** "How is it planted?" gate (Bekky, 2026-08-22) — first setup question that
   *  reveals only the relevant follow-ups. 'pot' shows pot type/size/drainage;
   *  'ground'/'raised_bed'/'hydroponic' hide the pot questions; 'unsure' shows
   *  the full set. Optional for backward compat with legacy saved setups. */
  plantedIn?: 'pot' | 'ground' | 'raised_bed' | 'hydroponic' | 'unsure';
  /** Follow-up to "In the ground": directly in the soil, in a garden bed, or in
   *  a raised bed. Soil (native vs custom brought-in) is chosen in the soil
   *  section, not here. (Bekky, 2026-08-22 — raised bed is a sub-detail.) */
  inGroundKind?: 'ground' | 'garden_bed' | 'raised_bed' | 'unsure';
  /** Hydroponic KIND of setup (Bekky, 2026-08-22) — minimal: water / hydro unit /
   *  custom. Serious hydro growers have a system and don't need the app. */
  hydroSetup?: 'water' | 'hydro_unit' | 'custom' | 'unsure';
  potSize?: 'small' | 'medium' | 'large' | 'extra_large' | 'custom' | 'not_applicable' | 'unsure';
  drainage?: 'has_holes' | 'no_holes' | 'not_applicable' | 'unsure';
  mediumType?: 'soil' | 'potting_mix' | 'cactus_mix' | 'orchid_bark' | 'leca' | 'coco_coir' | 'seed_starting' | 'garden_soil' | 'perlite' | 'vermiculite' | 'sphagnum_moss' | 'sand' | 'compost' | 'pumice' | 'charcoal' | 'worm_castings' | 'bark_chips' | 'lava_rock' | 'water' | 'hydroponic' | 'custom' | 'unsure' | 'general_purpose' | 'indoor_mix' | 'organic_all' | 'moisture_control' | 'succulent_cactus' | 'orchid' | 'vegetable' | 'acid_loving';
  mediumTypes?: string[];
  customMedium?: string;
  /** Custom-mix COMPONENTS (Bekky, 2026-08-22): the flat multi-select of DIY
   *  mix parts (potting soil, orchid bark, coco coir, LECA, perlite...). Always
   *  available alongside a store-bought mix. */
  customMediumComponents?: string[];
  topDressing?: string[];
  customTopDressing?: string;
  wateringMethod?: 'top_watering' | 'bottom_watering' | 'self_watering' | 'wick' | 'drip' | 'soak' | 'sprinkler' | 'watering_can' | 'hose' | 'mist' | 'humidifier' | 'custom' | 'unsure' | 'top' | 'bottom' | 'soaker_hose' | 'sprinkler_manual' | 'sprinkler_timer' | 'mister' | 'in_ground' | 'rainwater';
  wateringMethods?: string[];
  /** Watering STYLE (Bekky, 2026-08-22): top vs bottom — HOW water is applied,
   *  separate from the apparatus list. Optional. */
  wateringStyle?: 'top' | 'bottom' | 'unsure';
  /** Is the watering system automated? (Bekky, 2026-08-22) — a single yes/no
   *  that differentiates across all apparatus types, replacing manual/automated
   *  grouping. Optional for backward compat. */
  wateringAutomated?: boolean;
  customWatering?: string;
  usesDedicatedArtificialLight?: boolean;
  dedicatedArtificialLightTypes: ArtificialLightType[];
  dedicatedArtificialLightHoursPerDay?: number;
  dedicatedArtificialLightDistance?: string;
  /** What fertilizer this plant is fed with (Bekky, 2026-09-02) — the SINGLE
   *  canonical value synced across the setup form, fertilize cohort, and the
   *  fertilize complete-modal. Free text ("10-10-10", "liquid seaweed"),
   *  optional. */
  fertilizer?: string;
  /** SHARED CONTAINER (Bekky, 2026-09-04): mirrors SavedPlantProfile.sharedContainerId.
   *  A UUID shared by every plant in the same pot/container. Set when the user
   *  picks "Shared container" in setup. Absent = standalone. */
  sharedContainerId?: string;
  notes?: string;
  photoUris?: string[];
  setupPhotoUri?: string;
  setupPhotos?: GardenContextPhoto[];
  createdAt: string;
  updatedAt: string;
};

export type PlantEventType =
  | 'progress_photo'
  | 'new_leaf'
  | 'growth_measurement'
  | 'flowering'
  | 'fruiting'
  | 'pruning'
  | 'repotting'
  | 'pest_observation'
  | 'disease_observation'
  | 'treatment'
  | 'care_completed'
  | 'care_skipped'
  | 'identification'
  | 'setup_change'
  | 'space_change'
  | 'milestone'
  | 'custom'
  | 'yellow_leaves'
  | 'brown_tips'
  | 'drooping'
  | 'pest_signs'
  | 'slow_growth'
  | 'recovery'
  | 'improved_growth'
  | 'fertilized'
  | 'watered'
  | 'moved_location'
  | 'ai_finding'
  | 'follow_up'
  | 'issue_resolved';

export type PlantEventSource = 'user' | 'scan' | 'care' | 'ai' | 'system';

export type PlantEvent = {
  id: string;
  plantId: string;
  createdAt: string;
  eventDate: string;
  type: PlantEventType;
  title: string;
  note?: string;
  photoUri?: string;
  eventCategory?: 'action' | 'observation' | 'milestone' | 'progress_photo' | 'ai_finding' | 'follow_up' | 'custom';
  relatedCareTaskId?: string;
  relatedScanResultId?: string;
  source: PlantEventSource;
  updatedAt?: string;
  deletedAt?: string;
  isDeleted?: boolean;
  visibility?: 'private' | 'shared';
  trustRelevant?: boolean;
  resolvedAt?: string;
  resolutionStatus?: 'active' | 'treated' | 'resolved' | 'not_applicable';
};

export type CareTaskActionStatus = 'due' | 'completed' | 'snoozed' | 'skipped';

export type CareTaskSource =
  | 'plant_care_text'
  | 'care_schedule'
  | 'diagnosis';

export type CareTaskNotificationQuickAction =
  | 'complete'
  | 'snooze'
  | 'remind_later'
  | 'remind_after_work'
  | 'preferred_reminder_time';

export type CareTaskDetailMetadata = {
  plantId: string;
  spaceId?: string | null;
  taskType: string;
  dueDate?: string | null;
  source: CareTaskSource;
  relatedDiagnosisId?: string | null;
  relatedProgressPhotoIds?: string[];
  sensorContext?: Record<string, unknown> | null;
  weatherContext?: Record<string, unknown> | null;
  potSoilContext?: Record<string, unknown> | null;
  aiRecommendationId?: string | null;
};

export type CareReminderPreferences = {
  preferredReminderTime?: string | null;
  afterWorkReminderTime?: string | null;
};

export type PetType = 'cat' | 'dog' | 'bird' | 'rabbit' | 'other';
export type PetAccessMode = 'whole_home' | 'specific_spaces';
export type ChildAgeRange = 'baby_toddler' | 'young_child' | 'older_child_teen';
export type WishlistVisibility = 'private' | 'friends' | 'community';

/** Precision options when sharing a Field Diary entry/album (Bekky, 2026-08-25).
 *  'none' = no location at all (the hermit option). */
export type FieldSharePrecision = 'none' | 'place' | 'approx' | 'exact';

/** A location captured at scan time for a Field Diary entry. Stored privately
 *  with BOTH a friendly place label AND precise coords (re-finding / foraging).
 *  `customLabel` is a user-filled location override (Bekky, 2026-08-25). */
export type FieldLocation = {
  placeLabel?: string;
  customLabel?: string;
  latitude?: number;
  longitude?: number;
  approximateLatitude?: number;
  approximateLongitude?: number;
  nearbyCityOrZip?: string;
};

/** One organism found on a walk (Bekky, 2026-08-25). Holds ALL the scan photos
 *  (own copies, good resolution, expandable in a lightbox), the identification
 *  richness, and its own precision. */
export type FieldDiaryEntry = {
  id: string;
  createdAt: string;
  commonName: string;
  scientificName?: string;
  confidence: number;
  description?: string;
  notes?: string;
  scanResultId?: string;
  /** ALL photos from the scan, at good resolution, as own copies. */
  photos: { uri: string }[];
  /** Primary/cover photo (first photo). */
  coverPhotoUri?: string;
  location?: FieldLocation;
  /** Identification richness (mirrors WishlistItem rich fields). */
  taxonomy?: PlantSuggestionTaxonomy;
  commonNames?: string[];
  plantType?: PlantType;
  petSafety?: 'safe' | 'caution' | 'toxic' | 'unknown';
  sourceUrl?: string;
  referenceUrl?: string;
  referenceTitle?: string;
  referenceSource?: string;
  scanMode?: ScanMode;
  kingdom?: PlantIdentificationResult['kingdom'];
  edibility?: PlantIdentificationResult['edibility'];
  cultivable?: boolean;
  substrate?: string;
  /** Seed scan fields (Phase 3 continuation, 2026-09-08) — carried so a field
   *  seed find can be "Start Growing" directly (skip the scan). */
  seed?: SeedIdentificationResult;
  seedKind?: SeedKind;
  /** If this entry was scanned during an active excursion, link it to the map
   *  (Bekky, 2026-09-03). */
  excursionId?: string;
};

/** A FIELD ALBUM = one walk at one broad place (Bekky, 2026-08-25). Carries the
 *  broad place label + date; its entries each hold their own per-scan precision. */
export type FieldAlbum = {
  id: string;
  /** Album key = DATE + LOCATION (Bekky, 2026-09-02): `${YYYY-MM-DD}__${placeLabel}`.
   *  Two walks in the same city on different days get separate albums. Older
   *  albums persisted before this field may lack it — fall back to placeLabel. */
  albumKey?: string;
  /** Broad place label — the album spans a whole walk, so no single precise point. */
  placeLabel: string;
  /** The day(s) this album covers; albumKey is the preferred grouping key. */
  createdAt: string;
  entries: FieldDiaryEntry[];
  /** How many finds in the album (derived, but stored for quick display). */
  count?: number;
  /** Excursion mode, if this album was generated from a tracked trip
   *  (Bekky, 2026-09-03). */
  excursionMode?: ExcursionMode;
  /** The route polyline (ordered GPS points). */
  route?: ExcursionPoint[];
  /** Total distance in meters (Haversine sum). */
  distanceMeters?: number;
  /** Total duration in seconds. */
  durationSec?: number;
  /** When the excursion started / ended. */
  startedAt?: string;
  endedAt?: string;
  /** The excursion this album belongs to (links plant entries to the map). */
  excursionId?: string;
};


export type WishlistItem = {
  id: string;
  createdAt: string;
  source: 'scan';
  imageUri: string;
  commonName: string;
  scientificName?: string;
  confidence: number;
  description?: string;
  notes?: string;
  scanResultId?: string;
  locationContext?: string;
  nearbyCityOrZip?: string;
  /** Precise GPS captured at scan time (Bekky, 2026-09-08) — where the plant
   *  was scanned. Only set when the user's GPS precision is 'precise'. Drives
   *  the "Where I'm from" + Open in Google Maps link. */
  gpsLocation?: { latitude: number; longitude: number };
  propagation?: SavedPropagationInfo;
  wishlistVisibility: WishlistVisibility;
  /** Rich scan data carried through from the plant ID result (Bekky, 2026-08-25)
   *  so a wishlist detail can be as rich as a garden plant's Identity/About/Wiki.
   *  All optional — older wishlist items (saved before this) simply lack them. */
  taxonomy?: PlantSuggestionTaxonomy;
  commonNames?: string[];
  plantType?: PlantType;
  petSafety?: 'safe' | 'caution' | 'toxic' | 'unknown';
  /** Primary source/reference URL (e.g. Wikipedia page for the species). */
  sourceUrl?: string;
  referenceUrl?: string;
  referenceTitle?: string;
  referenceSource?: string;
  /** Fungus scan fields (mode='fungus') — carried so the wishlist detail shows
   *  kingdom + edibility + the always-on edibility disclaimer. */
  scanMode?: ScanMode;
  kingdom?: PlantIdentificationResult['kingdom'];
  edibility?: PlantIdentificationResult['edibility'];
  cultivable?: boolean;
  substrate?: string;
  /** Seed scan fields (Bekky, 2026-09-07, seed-intake feature) — carried so the
   *  wishlist detail keeps the rich seed-saving + germination profile. */
  seed?: SeedIdentificationResult;
  seedKind?: SeedKind;
};

export type HouseholdSafetyPreferences = {
  hasPets: boolean;
  petTypes: PetType[];
  petAccess: PetAccessMode;
  petAccessSpaceIds: string[];
  hasChildren: boolean;
  childAgeRanges: ChildAgeRange[];
};

export type ProfileCommunityPreferences = {
  birthday?: string | null;
  wishlistVisibility: WishlistVisibility;
  communityGiftingPreferences?: string | null;
};

export type GpsPrecision = 'rough' | 'precise' | 'skip';

/** Excursion mode — a tracked walk or ride (Bekky, 2026-09-03). */
export type ExcursionMode = 'walk' | 'ride';

/** A single GPS point on an excursion route. */
export type ExcursionPoint = {
  latitude: number;
  longitude: number;
  timestamp?: string;
};

/** Runtime state of an active excursion, persisted for crash recovery so a
 *  killed app can resume or offer to finalize the trip (Bekky, 2026-09-03). */
export type ExcursionState = {
  mode: ExcursionMode;
  startedAt: string;
  points: ExcursionPoint[];
  distanceMeters: number;
  excursionId: string;
  /** Prior gpsPrecision before the excursion's temp override, so stop can
   *  revert exactly (Bekky, 2026-09-03). Persisted so a killed app can still
   *  revert on finalize. */
  gpsOverridePrior?: GpsPrecision;
};

/** How the user's GARDEN location is captured (Bekky, 2026-08-23). This is the
 *  persistent location of the physical garden the user gets care advice for —
 *  SEPARATE from scan GPS (which is ephemeral, for identifying plants anywhere).
 *  Fetched ONCE (user-tap), persisted, user-updatable. Never auto re-fetched. */
export type GardenLocationMode = 'precise' | 'rough' | 'city_zip' | 'skip';

/**
 * SEASON PROFILE (Bekky, 2026-08-24): the months (0-11) in this location's
 * climate that count as "hot" and "cold" for climate-device reach logic, derived
 * once from Open-Meteo's 30-year climate normals and stored with the garden
 * location. Deterministic (no AI) — used as the month fallback in seasonModifier
 * when no daily forecast is available, and fed to the AI care context. This is
 * the "climate scope" as a BACKEND correlation (not a user chip): the reach chips
 * already encode when the user reaches for a device; this gives a location-accurate
 * sense of which months are actually hot/cold there (Da Lat's hot months differ
 * from Hanoi's or Phuket's). Re-fetched whenever the garden location is reset.
 */
export type SeasonProfile = {
  /** Months (0-11) whose 30-yr avg daily high >= HOT_MONTH_C. */
  hotMonths: number[];
  /** Months (0-11) whose 30-yr avg daily high <= COLD_MONTH_C. */
  coldMonths: number[];
  /** When this profile was fetched. */
  fetchedAt: string;
};

export type GardenLocation = {
  mode: GardenLocationMode;
  /** Captured coordinates (rounded per mode). */
  latitude?: number;
  longitude?: number;
  /** Human label — the city/ZIP text, or a friendly name for GPS captures. */
  label?: string;
  /** When the location was set/updated. */
  capturedAt?: string;
  /** Season profile derived from this location's climate (Bekky, 2026-08-24). */
  season?: SeasonProfile;
};

export type PrivacyPreferences = {
  approximateLocationContext?: string | null;
  photoPrivacy?: 'device_only' | 'ask_each_time' | 'cloud_allowed';
  progressPhotoHistoryEnabled?: boolean;
  aiPersonalizationEnabled?: boolean;
  gpsPrecision?: GpsPrecision;
  rememberGpsPrecision?: boolean;
  /** The persistent GARDEN location for weather-enhanced care + real-time
   *  alerts (Bekky, 2026-08-23). One-time fetch, user-updatable. */
  gardenLocation?: GardenLocation | null;
};

export type CarePreferences = {
  organicFirst?: boolean;
  petSafeCaution?: boolean;
  ediblePlantCaution?: boolean;
  shoppingStyle?: 'diy_first' | 'store_bought' | 'either';
};

export type AppSettings = {
  reminderPreferences: CareReminderPreferences;
  householdSafety: HouseholdSafetyPreferences;
  profileCommunity: ProfileCommunityPreferences;
  privacy: PrivacyPreferences;
  carePreferences: CarePreferences;
  /** Persisted Garden filter/sort so the user's view choice survives app
   *  open/close (Bekky, 2026-08-23 — "whatever the user chooses to sort and
   *  filter by should persist across app open/close"). */
  gardenView?: { plantFilter?: GardenPlantFilter; plantSort?: GardenPlantSort };
  updatedAt: string;
};

export type PlantNotificationType =
  | 'care_reminder'
  | 'diagnosis_update'
  | 'progress_photo_reminder'
  | 'scan_result'
  | 'achievement'
  | 'community_activity'
  | 'ai_recommendation';

export type PlantNotificationPriority = 'low' | 'normal' | 'high';

export type PlantNotificationCategory =
  | 'Action Needed'
  | 'Reminder'
  | 'Insight'
  | 'Achievement'
  | 'Community';

export type PlantNotificationRelatedEntityType =
  | 'plant'
  | 'care_task'
  | 'scan'
  | 'wishlist'
  | 'community'
  | 'none';

export type PlantNotificationActionType =
  | 'complete'
  | 'snooze'
  | 'remind_later'
  | 'open_related'
  | 'dismiss'
  | 'none';

export type PlantNotification = {
  id: string;
  type: PlantNotificationType;
  title: string;
  message: string;
  createdAt: string;
  scheduledFor?: string | null;
  relatedPlantId?: string | null;
  relatedTaskId?: string | null;
  category?: PlantNotificationCategory;
  reason?: string;
  relatedEntityType?: PlantNotificationRelatedEntityType;
  relatedEntityId?: string | null;
  actionLabel?: string;
  destination?: string;
  priority: PlantNotificationPriority;
  isRead: boolean;
  isDismissed: boolean;
  actionType: PlantNotificationActionType;
};

export type PlantNotificationState = {
  id: string;
  isRead: boolean;
  isDismissed: boolean;
  remindedAt?: string;
  snoozedUntil?: string;
  updatedAt: string;
};

export type CareTaskActionState = {
  taskId: string;
  plantId: string;
  spaceId?: string | null;
  taskType: string;
  dueDate?: string | null;
  source?: CareTaskSource;
  metadata?: CareTaskDetailMetadata;
  notificationQuickActions?: CareTaskNotificationQuickAction[];
  reminderPreferences?: CareReminderPreferences;
  status: CareTaskActionStatus;
  completedAt?: string;
  snoozedUntil?: string;
  skippedAt?: string;
  updatedAt: string;
};

export type GardenInfrastructureState = {
  rooms: RoomProfile[];
  roomOrderKeys: string[];
  plantSetups: PlantSetupProfile[];
  samplePlants: {
    id: string;
    name: string;
    status: string;
    care: string;
    label: string;
    spaceId?: string | null;
  }[];
  gardenOrderKeys: string[];
  sampleWatercolorIcons: Record<string, string>;
  samplePlantsSeeded?: boolean;
};

export type GardenSavedPlantState = {
  savedPlants: SavedPlantProfile[];
};

export type GardenWishlistState = {
  wishlistItems: WishlistItem[];
};
