// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import {create} from "zustand";

// the type of a reduced program
export type ReducedProgram = { phases: Record<string, unknown>[] };

export type GoalWithDepth = Record<string, unknown> & { level: number };

/**
 * the type definition of the programStore
 */
export type ProgramState = {
  // Basic store functionality:
  currentProgram: ReducedProgram;
  setProgramState: (state: ReducedProgram) => void;
  getProgramState: () => ReducedProgram;

  // Utility functions:
  // to avoid having to manually go through the entire state for every instance where data is required
  getPhaseIds: () => string[];
  getPhaseNames: () => string[];
  getNormsInPhase: (currentPhaseId: string) => Record<string, unknown>[];
  getGoalsInPhase: (currentPhaseId: string) => Record<string, unknown>[];
  getGoalsWithDepth: (currentPhaseId: string) => GoalWithDepth[];
  getTriggersInPhase: (currentPhaseId: string) => Record<string, unknown>[];
  // if more specific utility functions are needed they can be added here:
}

/**
 * the ProgramStore can be used to access all information of the most recently sent program,
 * it contains basic functions to set and get the current program.
 * And it contains some utility functions that allow you to easily gain access
 * to the norms, triggers and goals of a specific phase.
 */
const useProgramStore = create<ProgramState>((set, get) => ({
  currentProgram: { phases: [] as Record<string, unknown>[]},
  /**
   * sets the current program by cloning the provided program using a structuredClone
   */
  setProgramState: (program: ReducedProgram) => set({currentProgram: structuredClone(program)}),
  /**
   * gets the current program
   */
  getProgramState: () => get().currentProgram,

  // utility functions:
  /**
   * gets the ids of all phases in the program
   */
  getPhaseIds: () => get().currentProgram.phases.map(entry => entry["id"] as string),
   /**
   * gets the names of all phases in the program
   */
  getPhaseNames: () => get().currentProgram.phases.map((entry) => (entry["name"] as string)),
  /**
   * gets the norms for the provided phase
   */
  getNormsInPhase: (currentPhaseId) => {
    const program = get().currentProgram;
    const phase = program.phases.find(val => val["id"] === currentPhaseId);
    if (phase) {
      return phase["norms"] as Record<string, unknown>[];
    }
    throw new Error(`phase with id:"${currentPhaseId}" not found`)
  },
  /**
   * gets the goals for the provided phase
   */
  getGoalsInPhase: (currentPhaseId) => {
    const program = get().currentProgram;
    const phase = program.phases.find(val => val["id"] === currentPhaseId);
    if (phase) {
      return phase["goals"] as Record<string, unknown>[];
    }
    throw new Error(`phase with id:"${currentPhaseId}" not found`)
  },

  getGoalsWithDepth: (currentPhaseId: string) => {
    const program = get().currentProgram;
    const phase = program.phases.find(val => val["id"] === currentPhaseId);
    
    if (!phase) {
      throw new Error(`phase with id:"${currentPhaseId}" not found`);
    }

    const rootGoals = phase["goals"] as Record<string, unknown>[];
    const flatList: GoalWithDepth[] = [];

    const isGoal = (item: Record<string, unknown>) => {
      return item["plan"] !== undefined;
    };

    // Recursive helper function
    const traverse = (goals: Record<string, unknown>[], depth: number) => {
      goals.forEach((goal) => {
        // 1. Add the current goal to the list
        flatList.push({ ...goal, level: depth });

        // 2. Check for children
        const plan = goal["plan"] as Record<string, unknown> | undefined;
        
        if (plan && Array.isArray(plan["steps"])) {
           const steps = plan["steps"] as Record<string, unknown>[];
           
           // 3. FILTER: Only recurse on steps that are actually goals
           // If we just passed 'steps', we might accidentally add Actions/Speeches to the goal list
           const childGoals = steps.filter(isGoal);

           if (childGoals.length > 0) {
             traverse(childGoals, depth + 1);
           }
        }
      });
    };

    // Start traversal
    traverse(rootGoals, 0);

    return flatList;
  },
  /**
   * gets the triggers for the provided phase
   */
  getTriggersInPhase: (currentPhaseId) => {
    const program = get().currentProgram;
    const phase = program.phases.find(val => val["id"] === currentPhaseId);
    if (phase) {
      return phase["triggers"] as Record<string, unknown>[];
    }
    throw new Error(`phase with id:"${currentPhaseId}" not found`)
  }
}));

export default useProgramStore;