/**
 * School Settings store
 *
 * Holds the school-wide default grading scale + the school's list
 * of education-level configurations (level ↔ grading scale bindings
 * — feature/shapeshift). The auth store is the source of truth for
 * the user's role; this store is only concerned with the data.
 *
 * Backend: GET /api/school-settings and the per-resource CRUD
 * endpoints under /api/school-settings/*. PUT/POST/DELETE are
 * admin-only on the server; we still check role on the client to
 * hide pickers in the UI for non-admins.
 */

import { create } from 'zustand';
import api from './api';
import {
  DEFAULT_SCALE_KEY,
  scaleOptions,
  type ScaleOption,
  type GradeScale,
  SCALES,
} from '../lib/gradeScales';

/** Canonical education levels — keep in sync with the backend
 * `EDUCATION_LEVELS` map in server/src/controllers/schoolSettings.controller.js. */
export type EducationLevelKey = 'primary' | 'secondary' | 'high_school' | 'tertiary';

export interface EducationLevelOption {
  value: EducationLevelKey;
  label: string;
  description: string;
}

export const EDUCATION_LEVELS: Record<EducationLevelKey, EducationLevelOption> = {
  primary: {
    value: 'primary',
    label: 'Primary',
    description: 'Primary school (Grades 1–7). Terminology: "Subject" / "Grade".',
  },
  secondary: {
    value: 'secondary',
    label: 'Secondary (O Levels)',
    description: 'Secondary school O-Level (Forms 1–4 / Grades 8–11). Terminology: "Subject" / "Form / Class".',
  },
  high_school: {
    value: 'high_school',
    label: 'High School (A Levels)',
    description: 'High school A-Level (Forms 5–6 / Lower & Upper Sixth). Terminology: "Subject" / "Form / Class".',
  },
  tertiary: {
    value: 'tertiary',
    label: 'Tertiary',
    description: 'Tertiary institution. Terminology: "Course" / "Programme" / "Year/Semester".',
  },
};

export const DEFAULT_EDUCATION_LEVEL: EducationLevelKey = 'secondary';

export const educationLevelOptions = (): EducationLevelOption[] =>
  (Object.values(EDUCATION_LEVELS) as EducationLevelOption[]);

const isValidEducationLevel = (v: unknown): v is EducationLevelKey =>
  typeof v === 'string' &&
  Object.prototype.hasOwnProperty.call(EDUCATION_LEVELS, v);

/** Row returned by /api/school-settings/education-level-configs. */
export interface EducationLevelConfig {
  uid: string;
  level: EducationLevelKey;
  level_label: string;
  grading_scale: string;
  grading_scale_label: string;
  display_name: string | null;
  sort_order: number;
  is_active: boolean;
  is_default: boolean;
  created_at?: string;
  updated_at?: string;
}

export interface EducationLevelConfigInput {
  level: EducationLevelKey;
  grading_scale: string;
  display_name?: string | null;
  sort_order?: number;
  is_active?: boolean;
  is_default?: boolean;
}

interface SchoolSettingsState {
  defaultGradingScale: string;
  defaultGradingScaleLabel: string;
  options: ScaleOption[];
  /** Backward-compat alias for `options`. */
  scaleOptions: ScaleOption[];
  educationLevel: EducationLevelKey;
  educationLevelLabel: string;
  educationLevelOptions: EducationLevelOption[];
  educationLevelConfigs: EducationLevelConfig[];
  loaded: boolean;
  loading: boolean;
  /** Backward-compat alias for `loading`. */
  saving: boolean;
  error: string | null;

  fetchSettings: () => Promise<void>;
  fetchEducationLevelConfigs: (includeInactive?: boolean) => Promise<void>;
  createEducationLevelConfig: (input: EducationLevelConfigInput) => Promise<EducationLevelConfig>;
  updateEducationLevelConfig: (uid: string, patch: Partial<EducationLevelConfigInput>) => Promise<EducationLevelConfig>;
  deleteEducationLevelConfig: (uid: string) => Promise<void>;
  setDefaultLevel: (uid: string) => Promise<EducationLevelConfig>;
  setDefaultGradingScale: (key: string) => Promise<void>;
  setEducationLevel: (key: EducationLevelKey) => Promise<void>;
  reset: () => void;
}

const fallbackOption = (key: string) => ({
  key,
  label: SCALES[key]?.label || key,
  description: SCALES[key]?.description || '',
});

export const useSchoolSettingsStore = create<SchoolSettingsState>((set, get) => ({
  defaultGradingScale: DEFAULT_SCALE_KEY,
  defaultGradingScaleLabel: SCALES[DEFAULT_SCALE_KEY].label,
  options: scaleOptions(),
  scaleOptions: scaleOptions(),
  educationLevel: DEFAULT_EDUCATION_LEVEL,
  educationLevelLabel: EDUCATION_LEVELS[DEFAULT_EDUCATION_LEVEL].label,
  educationLevelOptions: educationLevelOptions(),
  educationLevelConfigs: [],
  loaded: false,
  loading: false,
  saving: false,
  error: null,

  fetchSettings: async () => {
    if (get().loading) return;
    set({ loading: true, saving: true, error: null });
    try {
      const [settingsRes, optionsRes, configsRes] = await Promise.all([
        api.get('/school-settings').catch(() => ({ data: {} as Record<string, string> })),
        api
          .get('/school-settings/scales')
          .catch(() => ({ data: scaleOptions() as ScaleOption[] })),
        api
          .get('/school-settings/education-level-configs?include_inactive=true')
          .catch(() => ({ data: [] as EducationLevelConfig[] })),
      ]);
      const data = (settingsRes.data || {}) as Record<string, string>;
      const opts = (optionsRes.data && Array.isArray(optionsRes.data) ? optionsRes.data : scaleOptions());
      const level = isValidEducationLevel(data.education_level)
        ? data.education_level
        : DEFAULT_EDUCATION_LEVEL;
      const configs = Array.isArray(configsRes.data) ? configsRes.data : [];
      
      const defaultCfg = configs.find(c => c.is_default && c.is_active);
      const key = defaultCfg
        ? defaultCfg.grading_scale
        : (typeof data.default_grading_scale === 'string' &&
           Object.prototype.hasOwnProperty.call(SCALES, data.default_grading_scale)
            ? data.default_grading_scale
            : DEFAULT_SCALE_KEY);

      set({
        defaultGradingScale: key,
        defaultGradingScaleLabel: SCALES[key]?.label || key,
        options: opts,
        scaleOptions: opts,
        educationLevel: level,
        educationLevelLabel: EDUCATION_LEVELS[level].label,
        educationLevelOptions: educationLevelOptions(),
        educationLevelConfigs: configs,
        loaded: true,
        loading: false,
        saving: false,
      });
    } catch (err: any) {
      set({
        error: err?.response?.data?.error || err?.message || 'Failed to load school settings',
        loading: false,
        saving: false,
        loaded: true,
        options: scaleOptions(),
        scaleOptions: scaleOptions(),
        educationLevelOptions: educationLevelOptions(),
      });
    }
  },

  fetchEducationLevelConfigs: async (includeInactive = false) => {
    set({ loading: true, error: null });
    try {
      const { data } = await api.get('/school-settings/education-level-configs', {
        params: includeInactive ? { include_inactive: 1 } : {},
      });
      set({ educationLevelConfigs: Array.isArray(data) ? data : [], loading: false });
    } catch (err: any) {
      set({
        loading: false,
        error: err?.response?.data?.error || err?.message || 'Failed to load education level configs',
      });
    }
  },

  createEducationLevelConfig: async (input) => {
    set({ saving: true, error: null });
    try {
      const { data } = await api.post('/school-settings/education-level-configs', input);
      const updatedConfigs = [...get().educationLevelConfigs, data].sort(
        (a, b) => a.sort_order - b.sort_order,
      );
      const defaultCfg = updatedConfigs.find(c => c.is_default && c.is_active);
      const key = defaultCfg ? defaultCfg.grading_scale : get().defaultGradingScale;
      set({
        educationLevelConfigs: updatedConfigs,
        defaultGradingScale: key,
        defaultGradingScaleLabel: SCALES[key]?.label || key,
        saving: false,
      });
      return data as EducationLevelConfig;
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to create education level config';
      set({ saving: false, error: message });
      throw new Error(message);
    }
  },

  updateEducationLevelConfig: async (uid, patch) => {
    set({ saving: true, error: null });
    try {
      const { data } = await api.put(`/school-settings/education-level-configs/${uid}`, patch);
      const updatedConfigs = get().educationLevelConfigs
        .map((c) => (c.uid === uid ? (data as EducationLevelConfig) : c))
        .sort((a, b) => a.sort_order - b.sort_order);
      const defaultCfg = updatedConfigs.find(c => c.is_default && c.is_active);
      const key = defaultCfg ? defaultCfg.grading_scale : get().defaultGradingScale;
      set({
        educationLevelConfigs: updatedConfigs,
        defaultGradingScale: key,
        defaultGradingScaleLabel: SCALES[key]?.label || key,
        saving: false,
      });
      return data as EducationLevelConfig;
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to update education level config';
      set({ saving: false, error: message });
      throw new Error(message);
    }
  },

  deleteEducationLevelConfig: async (uid) => {
    set({ saving: true, error: null });
    try {
      await api.delete(`/school-settings/education-level-configs/${uid}`);
      const updatedConfigs = get().educationLevelConfigs.filter((c) => c.uid !== uid);
      const defaultCfg = updatedConfigs.find(c => c.is_default && c.is_active);
      const key = defaultCfg ? defaultCfg.grading_scale : get().defaultGradingScale;
      set({
        educationLevelConfigs: updatedConfigs,
        defaultGradingScale: key,
        defaultGradingScaleLabel: SCALES[key]?.label || key,
        saving: false,
      });
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to delete education level config';
      set({ saving: false, error: message });
      throw new Error(message);
    }
  },

  setDefaultLevel: async (uid) => {
    set({ saving: true, error: null });
    try {
      // Single PUT; the server toggles is_default atomically.
      const { data } = await api.put(`/school-settings/education-level-configs/${uid}`, {
        is_default: true,
      });
      const updatedConfigs = get().educationLevelConfigs.map((c) => ({
        ...c,
        is_default: c.uid === uid,
      }));
      const defaultCfg = updatedConfigs.find(c => c.is_default && c.is_active);
      const key = defaultCfg ? defaultCfg.grading_scale : get().defaultGradingScale;
      // Shapeshift: the legacy `educationLevel` field (consumed by
      // `useEducationTerms()`) MUST track the active default config row,
      // otherwise the app keeps showing "Subject" after the admin promotes
      // Tertiary to default. Fall back to DEFAULT_EDUCATION_LEVEL if the
      // configs haven't loaded yet for some reason.
      const newLevel: EducationLevelKey = (defaultCfg?.level &&
        isValidEducationLevel(defaultCfg.level))
        ? defaultCfg.level
        : DEFAULT_EDUCATION_LEVEL;
      set({
        educationLevelConfigs: updatedConfigs,
        defaultGradingScale: key,
        defaultGradingScaleLabel: SCALES[key]?.label || key,
        educationLevel: newLevel,
        educationLevelLabel: defaultCfg?.level_label || EDUCATION_LEVELS[newLevel].label,
        saving: false,
      });
      return data as EducationLevelConfig;
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to set default education level';
      set({ saving: false, error: message });
      throw new Error(message);
    }
  },

  setDefaultGradingScale: async (key: string) => {
    if (!Object.prototype.hasOwnProperty.call(SCALES, key)) {
      throw new Error(`Unknown grading scale: ${key}`);
    }
    set({ loading: true, saving: true, error: null });
    try {
      const { data } = await api.put('/school-settings/default-grading-scale', { value: key });
      set({
        defaultGradingScale: data.value,
        defaultGradingScaleLabel: data.label || SCALES[key].label,
        loading: false,
        saving: false,
      });
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to update default';
      set({ loading: false, saving: false, error: message });
      throw new Error(message);
    }
  },

  setEducationLevel: async (key: EducationLevelKey) => {
    if (!isValidEducationLevel(key)) {
      throw new Error(`Unknown education level: ${key}`);
    }
    set({ loading: true, saving: true, error: null });
    try {
      const { data } = await api.put('/school-settings/education-level', { value: key });
      set({
        educationLevel: data.value,
        educationLevelLabel: data.label || EDUCATION_LEVELS[key].label,
        loading: false,
        saving: false,
      });
    } catch (err: any) {
      const message = err?.response?.data?.error || err?.message || 'Failed to update education level';
      set({ loading: false, saving: false, error: message });
      throw new Error(message);
    }
  },

  reset: () => {
    const opts = scaleOptions();
    return set({
      defaultGradingScale: DEFAULT_SCALE_KEY,
      defaultGradingScaleLabel: SCALES[DEFAULT_SCALE_KEY].label,
      options: opts,
      scaleOptions: opts,
      educationLevel: DEFAULT_EDUCATION_LEVEL,
      educationLevelLabel: EDUCATION_LEVELS[DEFAULT_EDUCATION_LEVEL].label,
      educationLevelOptions: educationLevelOptions(),
      educationLevelConfigs: [],
      loaded: false,
      loading: false,
      saving: false,
      error: null,
    });
  },
}));

export default useSchoolSettingsStore;

// Re-export the scale type for convenience.
export type { GradeScale };
export { fallbackOption };
