/**
 * GraduationService — cutting/seed graduation readiness.
 *
 * Graduation is never automatic: these helpers only identify when the profile
 * has enough evidence to offer the user a confirmation step.
 */
import type { PlantEvent } from '../../types/garden';
import type { SavedPlantProfile } from '../../types/plantScan';
import {
  cleanContextText,
  eventTimestamp,
  getLivePlantEvents,
  profileAgeDays,
} from './eventUtils';

export type GraduationReadiness = {
  ready: boolean;
  reason: string;
  signals: string[];
};

const CUTTING_MIN_AGE_DAYS = 14;
const NON_TREE_SEED_MIN_AGE_DAYS = 21;
const TREE_SEED_MIN_AGE_DAYS = 60;
const MAX_SIGNAL_LINES = 5;

const MILESTONE_GROWTH_TYPES = new Set([
  'recovery',
  'improved_growth',
  'new_leaf',
  'flowering',
  'fruiting',
  'milestone',
  'issue_resolved',
]);

const PROGRESS_GROWTH_TYPES = new Set([
  ...MILESTONE_GROWTH_TYPES,
  'growth_measurement',
  'progress_photo',
]);

// A clear cutting-specific sign can justify an early confirmation prompt.
const IMMEDIATE_CUTTING_SIGNAL_TYPES = new Set([
  'recovery',
  'improved_growth',
  'new_leaf',
  'progress_photo',
  'issue_resolved',
]);

function isMilestoneEvent(event: PlantEvent): boolean {
  return event.eventCategory === 'milestone' || event.type === 'milestone';
}

function isGrowthEvent(event: PlantEvent): boolean {
  return PROGRESS_GROWTH_TYPES.has(event.type) || isMilestoneEvent(event);
}

function signalLines(events: PlantEvent[]): string[] {
  return [...events]
    .sort((a, b) => (eventTimestamp(b) ?? 0) - (eventTimestamp(a) ?? 0))
    .slice(0, MAX_SIGNAL_LINES)
    .map(event => {
      const title = cleanContextText(event.title) || 'Plant progress';
      const note = cleanContextText(event.note);
      return note ? `${title} — ${note}` : title;
    });
}

/** Is this profile a cutting that has not yet graduated? */
export function isCuttingProfile(plant: SavedPlantProfile): boolean {
  return plant.plantType === 'cutting' || plant.growthStage === 'cutting';
}

/** Heuristic readiness check from a cutting's memory timeline. */
export function evaluateCuttingReadiness(plant: SavedPlantProfile): GraduationReadiness {
  const events = getLivePlantEvents(plant);
  const growthEvents = events.filter(isGrowthEvent);
  const signals = signalLines(growthEvents);
  const ageDays = profileAgeDays(plant.createdAt);

  const hasMilestone = growthEvents.some(isMilestoneEvent);
  const hasImmediateRootingSignal = growthEvents.some(
    event => IMMEDIATE_CUTTING_SIGNAL_TYPES.has(event.type),
  );
  const oldWithGrowth = ageDays >= CUTTING_MIN_AGE_DAYS && growthEvents.length > 0;

  if (hasMilestone || hasImmediateRootingSignal || oldWithGrowth) {
    return {
      ready: true,
      reason: hasMilestone
        ? 'You have logged a milestone for this cutting — it looks ready to become a full plant.'
        : hasImmediateRootingSignal
          ? 'This cutting shows signs of rooting and new growth.'
          : 'This cutting has had time to establish roots.',
      signals: signals.length
        ? signals
        : ['No explicit milestone yet, but enough time has passed.'],
    };
  }

  return {
    ready: false,
    reason: 'This cutting does not show signs of rooting yet. Keep logging progress photos so Pixie can tell when it’s ready.',
    signals,
  };
}

/** Build the SavedPlantProfile that results from cutting graduation. */
export function graduateCuttingProfile(plant: SavedPlantProfile): SavedPlantProfile {
  return {
    ...plant,
    plantType: plant.plantType === 'cutting' ? 'houseplant' : plant.plantType,
    growthStage: 'young_plant',
    updatedAt: new Date().toISOString(),
  };
}

/** Is this profile a planted seed that has not yet graduated? */
export function isSeedProfile(plant: SavedPlantProfile): boolean {
  return plant.plantType === 'garden_crop' && plant.growthStage === 'seedling';
}

/** Detect whether the saved seed result describes a tree. */
export function isTreeSeed(plant: SavedPlantProfile): boolean {
  const outcome = cleanContextText(plant.seed?.expectedOutcome).toLowerCase();
  const timeToFruit = cleanContextText(plant.seed?.timeToFruit).toLowerCase();
  const explicitlyTreeLike = /\b(?:tree|trees|sapling|orchard)\b/.test(`${outcome} ${timeToFruit}`);
  const multiYearFruitTimeline = /\b(?:\d+(?:\.\d+)?\s*(?:-|–|to)?\s*)?years?\b/.test(timeToFruit);
  return explicitlyTreeLike || multiYearFruitTimeline;
}

/**
 * A seedling needs both elapsed establishment time and visible growth evidence,
 * unless the gardener has explicitly logged a milestone. Trees use the longer
 * seedling-to-sapling threshold.
 */
export function evaluateSeedReadiness(plant: SavedPlantProfile): GraduationReadiness {
  const events = getLivePlantEvents(plant);
  const growthEvents = events.filter(isGrowthEvent);
  const signals = signalLines(growthEvents);
  const ageDays = profileAgeDays(plant.createdAt);
  const treeSeed = isTreeSeed(plant);
  const minimumAgeDays = treeSeed ? TREE_SEED_MIN_AGE_DAYS : NON_TREE_SEED_MIN_AGE_DAYS;
  const nextStage = treeSeed ? 'sapling' : 'young plant';

  const hasMilestone = growthEvents.some(isMilestoneEvent);
  const hasAgeAndGrowth = ageDays >= minimumAgeDays && growthEvents.length > 0;

  if (hasMilestone || hasAgeAndGrowth) {
    return {
      ready: true,
      reason: hasMilestone
        ? `You have logged a milestone for this seed — it looks ready to become a ${nextStage}.`
        : 'This seed shows signs of strong growth.',
      signals: signals.length
        ? signals
        : ['No explicit milestone yet, but enough time has passed.'],
    };
  }

  return {
    ready: false,
    reason: `This seed does not show enough growth yet. Keep logging progress photos so Pixie can tell when it's ready to become a ${nextStage}.`,
    signals,
  };
}

/** Trees go seedling → sapling; non-trees go seedling → young_plant. */
export function graduateSeedProfile(plant: SavedPlantProfile): SavedPlantProfile {
  return {
    ...plant,
    growthStage: isTreeSeed(plant) ? 'sapling' : 'young_plant',
    updatedAt: new Date().toISOString(),
  };
}
