// ── Constants ────────────────────────────────────────────────
const DAY_NAMES = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];
const DAY_SHORT = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб"];
const TYPE_KEYS = { "Лекция": "lec", "Практика": "prac", "Лаборатория": "lab", "Семинар": "sem" };

// ── API layer ─────────────────────────────────────────────────
window.API_BASE = window.location.hostname === "localhost" ? "http://localhost:8000" : "https://a-schedule-1.onrender.com";
window.API_TOKEN = localStorage.getItem("sched_token") || null;

function _apiFetch(path, options) {
  const opts = options || {};
  const headers = Object.assign({}, opts.headers || {});
  if (window.API_TOKEN) headers["Authorization"] = "Bearer " + window.API_TOKEN;
  return fetch(window.API_BASE + path, Object.assign({}, opts, { headers }))
    .then(res => {
      if (!res.ok) return res.text().then(t => { throw new Error(t || res.statusText); });
      return res.json();
    });
}

function _transformSlot(s) {
  const allGroups = s.lesson.groups || [];
  const mainGroups = allGroups.filter(g => g.is_main || !g.is_subgroup);
  const first = allGroups[0] || {};
  const primary = mainGroups.length > 0
    ? mainGroups[0].name
    : (first.parent_group || first.name || "");
  const langRaw = (first.language || "").toLowerCase();
  const isKaz = langRaw.startsWith("каз") || langRaw === "kz";
  const t = s.lesson.teacher;
  const teacher = t ? t.name : "—";
  const teacherFull = t
    ? ((isKaz ? t.title_kz : t.title_ru) || "") + t.name
    : "—";
  return {
    lessonDbId: s.lesson.db_id,
    day: s.day_of_week,
    shift: s.shift,
    lessonNumber: s.lesson_number,
    time: s.time_string,
    discipline: s.lesson.discipline_name,
    lessonType: s.lesson.lesson_type,
    teacher,
    teacherFull,
    language: first.language || "",
    room: s.room.id,
    roomObj: s.room,
    groups: allGroups.map(g => g.name),
    primary,
  };
}

// ── Initial empty state ───────────────────────────────────────
window.SCHED_DATA = {
  DAY_NAMES, DAY_SHORT, TYPE_KEYS,
  SCHEDULE: [], UNSCHEDULED: [], CONFLICTS: [],
  STATS: { rooms: 0, lessons: 0, scheduled: 0, unscheduled: 0, conflicts: 0, groups: 0, teachers: 0, departments: 0 },
  LOGS: [],
  GROUPS: [], TEACHERS: [], ROOMS: [],
};

// ── API methods ───────────────────────────────────────────────
window.API = {
  get(path) {
    return _apiFetch(path);
  },
  post(path, body) {
    return _apiFetch(path, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
  },
  put(path, body) {
    return _apiFetch(path, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
  },
  upload(path, file, fieldName) {
    const fd = new FormData();
    fd.append(fieldName || "file", file);
    return _apiFetch(path, { method: "POST", body: fd });
  },
  loadData() {
    return _apiFetch("/api/schedule/pending").catch(() => []).then(pendingRaw => {
      const hasPending = Array.isArray(pendingRaw) && pendingRaw.length > 0;
      return Promise.all([
        hasPending ? Promise.resolve(pendingRaw) : _apiFetch("/api/schedule"),
        hasPending
          ? _apiFetch("/api/schedule/pending/unscheduled").catch(() => [])
          : _apiFetch("/api/schedule/unscheduled"),
        _apiFetch("/api/stats"),
        _apiFetch("/api/schedule/pending/conflicts").catch(() => []),
        _apiFetch("/api/rooms"),
      ]).then(([scheduleRaw, unscheduledRaw, statsRaw, conflictsRaw, roomsRaw]) => {
        _applyFull(scheduleRaw, unscheduledRaw, statsRaw, conflictsRaw, roomsRaw, hasPending);
      });
    });
  },

  // After move: update schedule locally, apply conflicts+unscheduled from POST response (0 GETs)
  applyMoveResponse(lessonId, fromDay, fromShift, fromLesson, toDay, toShift, toLesson, response) {
    const prev = window.SCHED_DATA;
    const SCHEDULE = prev.SCHEDULE.map(s =>
      s.lessonDbId === lessonId && s.day === fromDay && s.shift === fromShift && s.lessonNumber === fromLesson
        ? { ...s, day: toDay, shift: toShift, lessonNumber: toLesson }
        : s
    );
    _applyResponseData(SCHEDULE, response);
  },

  // After delete: remove slot locally, apply conflicts+unscheduled from POST response (0 GETs)
  applyDeleteResponse(lessonId, day, shift, lessonNumber, response) {
    const prev = window.SCHED_DATA;
    const SCHEDULE = prev.SCHEDULE.filter(s =>
      !(s.lessonDbId === lessonId && s.day === day && s.shift === shift && s.lessonNumber === lessonNumber)
    );
    _applyResponseData(SCHEDULE, response);
  },

  // After add: fetch updated schedule (need full slot data), apply conflicts+unscheduled from POST response (1 GET)
  reloadScheduleAfterAdd(response) {
    const hasPending = window.SCHED_DATA.isPending;
    return _apiFetch(hasPending ? "/api/schedule/pending" : "/api/schedule").then(scheduleRaw => {
      _applyResponseData(scheduleRaw.map(_transformSlot), response);
    });
  },

  // After start-edit: load pending schedule + conflicts + unscheduled (3 GETs, skip rooms/stats)
  reloadAfterStartEdit() {
    return Promise.all([
      _apiFetch("/api/schedule/pending"),
      _apiFetch("/api/schedule/pending/unscheduled").catch(() => []),
      _apiFetch("/api/schedule/pending/conflicts").catch(() => []),
    ]).then(([scheduleRaw, unscheduledRaw, conflictsRaw]) => {
      const prev = window.SCHED_DATA;
      const SCHEDULE = scheduleRaw.map(_transformSlot);
      const CONFLICTS = conflictsRaw.map(_transformConflict);
      const UNSCHEDULED = unscheduledRaw.map((u, i) => ({
        dbId: u.db_id || (9000 + i),
        discipline: u.discipline_name,
        lessonType: u.lesson_type,
        groups: u.groups,
        remainingSlots: u.remaining_slots,
        remainingHours: u.remaining_hours,
      }));
      window.SCHED_DATA = Object.assign({}, prev, {
        SCHEDULE, CONFLICTS, UNSCHEDULED,
        isPending: true,
        STATS: Object.assign({}, prev.STATS, {
          scheduled: SCHEDULE.length,
          unscheduled: UNSCHEDULED.length,
          conflicts: CONFLICTS.length,
        }),
      });
    });
  },
};

function _applyFull(scheduleRaw, unscheduledRaw, statsRaw, conflictsRaw, roomsRaw, hasPending) {
  const SCHEDULE = scheduleRaw.map(_transformSlot);
  const groupSet = new Set();
  const teacherSet = new Set();
  const teacherFullMap = {};
  SCHEDULE.forEach(s => {
    if (s.primary) groupSet.add(s.primary);
    if (s.teacher && s.teacher !== "—") {
      teacherSet.add(s.teacher);
      if (s.teacherFull && s.teacherFull !== s.teacher) teacherFullMap[s.teacher] = s.teacherFull;
    }
  });
  const GROUPS = [...groupSet].sort();
  const TEACHERS = [...teacherSet].sort();
  const UNSCHEDULED = unscheduledRaw.map((u, i) => ({
    dbId: u.db_id || (9000 + i),
    discipline: u.discipline_name,
    lessonType: u.lesson_type,
    groups: u.groups,
    remainingSlots: u.remaining_slots,
    remainingHours: u.remaining_hours,
  }));
  const CONFLICTS = conflictsRaw.map(_transformConflict);
  window.SCHED_DATA = {
    DAY_NAMES, DAY_SHORT, TYPE_KEYS,
    SCHEDULE, UNSCHEDULED, CONFLICTS,
    isPending: hasPending,
    STATS: {
      rooms: statsRaw.rooms,
      lessons: statsRaw.lessons,
      scheduled: hasPending ? SCHEDULE.length : statsRaw.scheduled_slots,
      totalSlots: statsRaw.total_slots || 0,
      unscheduled: UNSCHEDULED.length,
      conflicts: CONFLICTS.length,
      groups: GROUPS.length,
      teachers: TEACHERS.length,
      departments: 0,
    },
    LOGS: [],
    GROUPS, TEACHERS, TEACHER_FULL: teacherFullMap, ROOMS: roomsRaw,
    lastUpdated: statsRaw.committed_at ? new Date(statsRaw.committed_at) : null,
  };
}

function _applyResponseData(SCHEDULE, response) {
  const prev = window.SCHED_DATA;
  const conflictsRaw = (response && response.conflicts) || [];
  const unscheduledRaw = (response && response.unscheduled) || null;
  const CONFLICTS = conflictsRaw.map(c => ({
    type: c.type,
    description: c.description,
    slotA: [c.slot_a_day, c.slot_a_shift, c.slot_a_lesson],
    slotB: [c.slot_b_day, c.slot_b_shift, c.slot_b_lesson],
    lessonAId: c.lesson_a_db_id ?? null,
    lessonBId: c.lesson_b_db_id ?? null,
  }));
  const UNSCHEDULED = unscheduledRaw
    ? unscheduledRaw.map((u, i) => ({
        dbId: u.db_id || (9000 + i),
        discipline: u.discipline_name,
        lessonType: u.lesson_type,
        groups: u.groups,
        remainingSlots: u.remaining_slots,
        remainingHours: u.remaining_hours,
      }))
    : prev.UNSCHEDULED;
  window.SCHED_DATA = Object.assign({}, prev, {
    SCHEDULE,
    UNSCHEDULED,
    CONFLICTS,
    STATS: Object.assign({}, prev.STATS, {
      scheduled: SCHEDULE.length,
      unscheduled: UNSCHEDULED.length,
      conflicts: CONFLICTS.length,
    }),
  });
}

function _transformConflict(c) {
  return {
    type: c.type,
    description: c.description,
    slotA: [c.slot_a_day, c.slot_a_shift, c.slot_a_lesson],
    slotB: [c.slot_b_day, c.slot_b_shift, c.slot_b_lesson],
    lessonAId: c.lesson_a_db_id ?? null,
    lessonBId: c.lesson_b_db_id ?? null,
  };
}
