export const API_BASE = import.meta.env.VITE_API_BASE || 'https://sv.rikkeiraia.org/api';

export function getWsUrl(path: string): string {
  try {
    if (API_BASE.startsWith('http://') || API_BASE.startsWith('https://')) {
      const url = new URL(API_BASE);
      const protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
      return `${protocol}//${url.host}${path}`;
    }
  } catch (e) {
    // ignore
  }
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  return `${protocol}//${window.location.host}${path}`;
}


function getToken(): string | null {
  return localStorage.getItem('sc_staff_token');
}

export async function staffFetch(path: string, init: RequestInit = {}): Promise<Response> {
  const headers = new Headers(init.headers);
  const token = getToken();
  if (token) headers.set('Authorization', `Bearer ${token}`);
  if (init.body && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }
  return fetch(`${API_BASE}${path}`, { ...init, headers });
}

async function parseError(res: Response, fallback: string): Promise<never> {
  const errData = await res.json().catch(() => ({}));
  const msg = (errData as { error?: string }).error;
  if (msg) throw new Error(msg);
  if (res.status === 413) throw new Error('File quá lớn (tối đa 100MB)');
  throw new Error(`${fallback} (HTTP ${res.status})`);
}

export interface StaffUser {
  id: number;
  email: string;
  fullName: string;
  mustChangePassword: boolean;
}

export const apiAuth = {
  provision: async (email: string): Promise<{ ok: boolean; message: string }> => {
    const res = await fetch(`${API_BASE}/auth/provision`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
    });
    if (!res.ok) await parseError(res, 'Provision failed');
    return res.json();
  },
  login: async (email: string, password: string): Promise<{ token: string; staff: StaffUser; mustChangePassword: boolean }> => {
    const res = await fetch(`${API_BASE}/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    });
    if (!res.ok) await parseError(res, 'Đăng nhập thất bại');
    return res.json();
  },
  me: async (): Promise<{ staff: StaffUser }> => {
    const res = await staffFetch('/auth/me');
    if (!res.ok) await parseError(res, 'Unauthorized');
    return res.json();
  },
  changePassword: async (oldPassword: string, newPassword: string): Promise<{ ok: boolean; token: string; staff: StaffUser }> => {
    const res = await staffFetch('/auth/change-password', {
      method: 'POST',
      body: JSON.stringify({ oldPassword, newPassword }),
    });
    if (!res.ok) await parseError(res, 'Đổi mật khẩu thất bại');
    return res.json();
  },
  forgotPassword: async (email: string): Promise<{ ok: boolean; message: string }> => {
    const res = await fetch(`${API_BASE}/auth/forgot-password`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
    });
    if (!res.ok) await parseError(res, 'Gửi mail thất bại');
    return res.json();
  },
  resetPassword: async (token: string, newPassword: string): Promise<{ ok: boolean; message: string }> => {
    const res = await fetch(`${API_BASE}/auth/reset-password`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token, newPassword }),
    });
    if (!res.ok) await parseError(res, 'Đặt lại mật khẩu thất bại');
    return res.json();
  },
};

export interface GitHubStatus {
  connected: boolean;
  githubLogin?: string;
  connectedAt?: string;
  scope?: string;
  canDeleteRepos?: boolean;
}

export const apiGitHub = {
  status: async (): Promise<GitHubStatus> => {
    const res = await staffFetch('/auth/github/status');
    if (!res.ok) await parseError(res, 'Không tải trạng thái GitHub');
    return res.json();
  },
  authorizeUrl: async (): Promise<{ authorizeUrl: string }> => {
    const res = await staffFetch('/auth/github/authorize');
    if (!res.ok) await parseError(res, 'Không bắt đầu OAuth GitHub');
    return res.json();
  },
  disconnect: async () => {
    const res = await staffFetch('/auth/github', { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Ngắt kết nối GitHub thất bại');
    return res.json();
  },
};

export interface GitHubRepoItem {
  key: string;
  ownerLogin: string;
  repoName: string;
  repoHtmlUrl: string;
  private?: boolean;
  examRoomId?: number;
  examRoomName?: string;
  studentLabel?: string;
  publishMode?: 'room' | 'student' | '';
  createdAt: string;
  updatedAt?: string;
  fromSimpleCare?: boolean;
}

export const apiGitHubRepos = {
  list: async (): Promise<{ data: GitHubRepoItem[]; total: number; githubLogin?: string }> => {
    const res = await staffFetch('/auth/github/repos');
    if (!res.ok) await parseError(res, 'Không tải danh sách repo');
    return res.json();
  },
  bulkDelete: async (repos: string[]) => {
    const res = await staffFetch('/auth/github/repos/bulk-delete', {
      method: 'POST',
      body: JSON.stringify({ repos }),
    });
    if (!res.ok) await parseError(res, 'Xóa repo thất bại');
    return res.json() as Promise<{ ok: boolean; deleted: number; failures?: string[]; message: string }>;
  },
};

export interface EmailDomainItem {
  id: number;
  domain: string;
  isActive: boolean;
}

export const apiAdmin = {
  listEmailDomains: async (): Promise<{ data: EmailDomainItem[] }> => {
    const res = await staffFetch('/admin/email-domains');
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  addEmailDomain: async (domain: string) => {
    const res = await staffFetch('/admin/email-domains', { method: 'POST', body: JSON.stringify({ domain }) });
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  deleteEmailDomain: async (id: number) => {
    const res = await staffFetch(`/admin/email-domains/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
};

export interface ChatStudent {
  studentRkId: number;
  fullName: string;
  studentCode: string;
  email: string;
  online: boolean;
}

export interface ChatMessage {
  id: number;
  staffId: number;
  targetStaffId?: number;
  studentRkId: number;
  senderRole: 'staff' | 'student';
  body: string;
  createdAt: string;
  staffName?: string;
}

export interface ChatConversation {
  studentRkId: number;
  fullName: string;
  studentCode: string;
  lastMessage: string;
  lastAt: string;
  unread: number;
}

export const apiChat = {
  listConversations: async (): Promise<{ data: ChatConversation[] }> => {
    const res = await staffFetch('/chat/conversations');
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  searchStudents: async (q = ''): Promise<{ data: ChatStudent[] }> => {
    const params = q ? `?q=${encodeURIComponent(q)}` : '';
    const res = await staffFetch(`/chat/students${params}`);
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  listMessages: async (studentRkId: number): Promise<{ data: ChatMessage[] }> => {
    const res = await staffFetch(`/chat/messages/${studentRkId}`);
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  sendMessage: async (studentRkId: number, body: string): Promise<{ ok: boolean; data: ChatMessage }> => {
    const res = await staffFetch('/chat/messages', {
      method: 'POST',
      body: JSON.stringify({ studentRkId, body }),
    });
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
};

export interface ClassItem {
  rkId: number;
  name: string;
  classCode: string;
  type: string;
  studentCount: number;
  specializeRkId?: number;
  specializeName?: string;
  systemRkId?: number;
  systemCode?: string;
  systemName?: string;
  isStudying: boolean;
  courses: { courseRkId: number; courseName: string }[];
}

export interface StudentItem {
  id: number;
  rkId: number;
  studentCode: string;
  fullName: string;
  phone?: string;
  email: string;
  dateOfBirth?: string;
  gender?: number;
  status?: string;
  location?: string;
  systemId?: number;
  systemName?: string;
  avatar?: string;
}

export interface StatsResponse {
  totalClasses: number;
  activeClasses: number;
  totalStudents: number;
}

export interface GuideLinkItem {
  id: number;
  createdAt: string;
  updatedAt: string;
  title: string;
  url: string;
}

export interface AppDownloadItem {
  id: number;
  createdAt: string;
  updatedAt: string;
  name: string;
  description: string;
  downloadUrl: string;
  platform: string;
}

export interface AppGuideItem {
  id: number;
  createdAt: string;
  updatedAt: string;
  title: string;
  url: string;
}

export interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  pageSize: number;
}

export interface SyncStatus {
  running: boolean;
  done: boolean;
  error?: string;
  total: number;
  synced: number;
  updatedAt: number;
  page?: number;
  pageSize?: number;
}

export interface ClassScheduleItem {
  id?: number;
  classRkId?: number;
  dayOfWeek: number;
  period: number;
  startTime: string;
  endTime: string;
  courseId: number;
  courseName: string;
  isActive: boolean;
}

export interface ClassCourseItem {
  id: number;
  name: string;
  courseCode?: string;
}

export interface AttendanceRow {
  studentRkId: number;
  studentCode: string;
  fullName: string;
  email: string;
  status: number;
  statusLabel: string;
  onlineMinutes: number;
  statusEditedByTeacher: boolean;
  pushedToQldtAt?: string;
}

export interface AttendanceShiftInfo {
  startTime?: string;
  endTime?: string;
  courseId?: number;
  courseName?: string;
  isActive?: boolean;
  pushedToQldtAt?: string;
  qldtDirty?: boolean;
}

export const ATTENDANCE_STATUS_OPTIONS = [
  { value: 0, label: 'Nghỉ không phép', short: 'NKP' },
  { value: 1, label: 'Nghỉ có phép', short: 'NCP' },
  { value: 2, label: 'Nghỉ nửa buổi', short: 'Nửa buổi' },
  { value: 3, label: 'Đi học muộn', short: 'Muộn' },
  { value: 4, label: 'Đi học đầy đủ', short: 'Đầy đủ' },
] as const;

export function attendanceStatusClass(status: number): string {
  return `attendance-status--${status}`;
}

export function attendanceStatusLabel(status: number): string {
  return ATTENDANCE_STATUS_OPTIONS.find(o => o.value === status)?.label ?? `Trạng thái ${status}`;
}

export interface StudentSessionLogItem {
  id: number;
  studentRkId: number;
  studentCode: string;
  fullName: string;
  email: string;
  onlineSeconds: number;
  offlineSeconds: number;
  wifiSsids: string;
  lastActiveAt?: string;
}

export const api = {
  getStats: async (): Promise<StatsResponse> => {
    const res = await staffFetch('/stats');
    if (!res.ok) throw new Error('Failed to fetch dashboard stats');
    return res.json();
  },

  listGuideLinks: async (): Promise<{ data: GuideLinkItem[] }> => {
    const res = await staffFetch('/guide-links');
    if (!res.ok) throw new Error('Không thể tải danh sách tài liệu');
    return res.json();
  },
  createGuideLink: async (title: string, url: string): Promise<{ data: GuideLinkItem }> => {
    const res = await staffFetch('/guide-links', {
      method: 'POST',
      body: JSON.stringify({ title, url }),
    });
    if (!res.ok) await parseError(res, 'Thêm tài liệu thất bại');
    return res.json();
  },
  updateGuideLink: async (id: number, title: string, url: string): Promise<{ data: GuideLinkItem }> => {
    const res = await staffFetch(`/guide-links/${id}`, {
      method: 'PUT',
      body: JSON.stringify({ title, url }),
    });
    if (!res.ok) await parseError(res, 'Cập nhật tài liệu thất bại');
    return res.json();
  },
  deleteGuideLink: async (id: number): Promise<{ ok: boolean }> => {
    const res = await staffFetch(`/guide-links/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa tài liệu thất bại');
    return res.json();
  },

  listAppDownloads: async (): Promise<{ data: AppDownloadItem[] }> => {
    const res = await staffFetch('/app-downloads');
    if (!res.ok) throw new Error('Không thể tải danh sách ứng dụng');
    return res.json();
  },
  createAppDownload: async (name: string, description: string, downloadUrl: string, platform: string): Promise<{ data: AppDownloadItem }> => {
    const res = await staffFetch('/app-downloads', {
      method: 'POST',
      body: JSON.stringify({ name, description, downloadUrl, platform }),
    });
    if (!res.ok) await parseError(res, 'Thêm ứng dụng thất bại');
    return res.json();
  },
  updateAppDownload: async (id: number, name: string, description: string, downloadUrl: string, platform: string): Promise<{ data: AppDownloadItem }> => {
    const res = await staffFetch(`/app-downloads/${id}`, {
      method: 'PUT',
      body: JSON.stringify({ name, description, downloadUrl, platform }),
    });
    if (!res.ok) await parseError(res, 'Cập nhật ứng dụng thất bại');
    return res.json();
  },
  deleteAppDownload: async (id: number): Promise<{ ok: boolean }> => {
    const res = await staffFetch(`/app-downloads/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa ứng dụng thất bại');
    return res.json();
  },

  listAppGuides: async (): Promise<{ data: AppGuideItem[] }> => {
    const res = await staffFetch('/app-guides');
    if (!res.ok) throw new Error('Không thể tải danh sách tài liệu hướng dẫn');
    return res.json();
  },
  createAppGuide: async (title: string, url: string): Promise<{ data: AppGuideItem }> => {
    const res = await staffFetch('/app-guides', {
      method: 'POST',
      body: JSON.stringify({ title, url }),
    });
    if (!res.ok) await parseError(res, 'Thêm tài liệu hướng dẫn thất bại');
    return res.json();
  },
  updateAppGuide: async (id: number, title: string, url: string): Promise<{ data: AppGuideItem }> => {
    const res = await staffFetch(`/app-guides/${id}`, {
      method: 'PUT',
      body: JSON.stringify({ title, url }),
    });
    if (!res.ok) await parseError(res, 'Cập nhật tài liệu hướng dẫn thất bại');
    return res.json();
  },
  deleteAppGuide: async (id: number): Promise<{ ok: boolean }> => {
    const res = await staffFetch(`/app-guides/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa tài liệu hướng dẫn thất bại');
    return res.json();
  },

  getClasses: async (params: {
    page: number;
    pageSize: number;
    q?: string;
    systemRkId?: number;
    studyingOnly?: boolean;
  }): Promise<PaginatedResponse<ClassItem>> => {
    const query = new URLSearchParams({
      page: String(params.page),
      pageSize: String(params.pageSize),
    });
    if (params.q) query.set('q', params.q);
    if (params.systemRkId) query.set('systemRkId', String(params.systemRkId));
    if (params.studyingOnly) query.set('studyingOnly', 'true');

    const res = await staffFetch(`/classes?${query}`);
    if (!res.ok) throw new Error('Failed to fetch classes');
    return res.json();
  },

  updateClassStudying: async (rkId: number, isStudying: boolean): Promise<any> => {
    const res = await staffFetch(`/classes/${rkId}/studying`, {
      method: 'PATCH',
      body: JSON.stringify({ isStudying }),
    });
    if (!res.ok) {
      const errData = await res.json().catch(() => ({}));
      throw new Error(errData.error || 'Failed to update class studying status');
    }
    return res.json();
  },

  getClassStudents: async (rkId: number): Promise<{ data: StudentItem[]; total: number }> => {
    const res = await staffFetch(`/classes/${rkId}/students`);
    if (!res.ok) throw new Error('Failed to fetch class students roster');
    return res.json();
  },

  getClass: async (rkId: number): Promise<ClassItem> => {
    const res = await staffFetch(`/classes/${rkId}`);
    if (!res.ok) throw new Error('Failed to fetch class');
    return res.json();
  },

  getStudents: async (params: {
    page: number;
    pageSize: number;
    q?: string;
  }): Promise<PaginatedResponse<StudentItem>> => {
    const query = new URLSearchParams({
      page: String(params.page),
      pageSize: String(params.pageSize),
    });
    if (params.q) query.set('q', params.q);

    const res = await staffFetch(`/students?${query}`);
    if (!res.ok) throw new Error('Failed to fetch students');
    return res.json();
  },

  startClassesSync: async (): Promise<{ ok: boolean; message: string }> => {
    const res = await staffFetch('/sync/classes/start', { method: 'POST' });
    if (!res.ok) {
      const errData = await res.json().catch(() => ({}));
      throw new Error(errData.error || 'Failed to start classes sync');
    }
    return res.json();
  },

  getClassesSyncStatus: async (): Promise<SyncStatus> => {
    const res = await staffFetch('/sync/classes/status');
    if (!res.ok) throw new Error('Failed to get classes sync status');
    return res.json();
  },

  startStudentsSync: async (): Promise<{ ok: boolean; message: string }> => {
    const res = await staffFetch('/sync/students/start', { method: 'POST' });
    if (!res.ok) {
      const errData = await res.json().catch(() => ({}));
      throw new Error(errData.error || 'Failed to start students sync');
    }
    return res.json();
  },

  getStudentsSyncStatus: async (): Promise<SyncStatus> => {
    const res = await staffFetch('/sync/students/status');
    if (!res.ok) throw new Error('Failed to get students sync status');
    return res.json();
  },
};

// Export individual learning functions to simplify imports in components
export const apiFetchActiveSchedules = async (): Promise<{ data: any[] }> => {
  const res = await staffFetch('/classes/schedules');
  if (!res.ok) throw new Error('Failed to fetch active schedules');
  return res.json();
};

export const apiFetchClassSchedule = async (rkId: number): Promise<{ data: ClassScheduleItem[] }> => {
  const res = await staffFetch(`/classes/${rkId}/schedule`);
  if (!res.ok) throw new Error('Failed to fetch class schedule');
  return res.json();
};

export const apiSaveClassSchedule = async (rkId: number, schedules: ClassScheduleItem[]): Promise<any> => {
  const res = await staffFetch(`/classes/${rkId}/schedule`, {
    method: 'POST',
    body: JSON.stringify({ schedules }),
  });
  if (!res.ok) throw new Error('Failed to save class schedule');
  return res.json();
};

export const apiDeleteClassSchedule = async (rkId: number): Promise<any> => {
  const res = await staffFetch(`/classes/${rkId}/schedule`, { method: 'DELETE' });
  if (!res.ok) throw new Error('Failed to delete class schedule');
  return res.json();
};

export const apiFetchAllowedApps = async (rkId: number): Promise<{ classRkId: number; keywords: string }> => {
  const res = await staffFetch(`/classes/${rkId}/allowed-apps`);
  if (!res.ok) throw new Error('Failed to fetch allowed apps keywords');
  return res.json();
};

export const apiSaveAllowedApps = async (rkId: number, keywords: string): Promise<any> => {
  const res = await staffFetch(`/classes/${rkId}/allowed-apps`, {
    method: 'POST',
    body: JSON.stringify({ keywords }),
  });
  if (!res.ok) throw new Error('Failed to save allowed apps keywords');
  return res.json();
};

export interface AppPoolItem {
  id: number;
  processName: string;
  windowTitle: string;
  keyword: string;
  hitCount: number;
  lastSeenAt: string;
  lastStudentRkId?: number;
  lastClassRkId?: number;
}

export const apiFetchAppPool = async (q = '', limit = 50): Promise<{ data: AppPoolItem[]; q?: string; limit?: number }> => {
  const params = new URLSearchParams();
  if (q.trim()) params.set('q', q.trim());
  params.set('limit', String(limit));
  const query = params.toString() ? `?${params.toString()}` : '';
  const res = await staffFetch(`/app-pool${query}`);
  if (!res.ok) throw new Error('Failed to fetch app pool');
  return res.json();
};

export interface AppTemplateItem {
  id: number;
  name: string;
  description: string;
  keywords: string;
  createdAt: string;
  updatedAt: string;
}

export const apiAppTemplates = {
  list: async (): Promise<{ data: AppTemplateItem[] }> => {
    const res = await staffFetch('/app-templates');
    if (!res.ok) throw new Error('Không tải được khung ứng dụng');
    return res.json();
  },
  create: async (body: { name: string; description?: string; keywords: string }) => {
    const res = await staffFetch('/app-templates', { method: 'POST', body: JSON.stringify(body) });
    if (!res.ok) await parseError(res, 'Không tạo được khung');
    return res.json();
  },
  update: async (id: number, body: { name?: string; description?: string; keywords?: string }) => {
    const res = await staffFetch(`/app-templates/${id}`, { method: 'PATCH', body: JSON.stringify(body) });
    if (!res.ok) await parseError(res, 'Không cập nhật được khung');
    return res.json();
  },
  delete: async (id: number) => {
    const res = await staffFetch(`/app-templates/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Không xóa được khung');
    return res.json();
  },
};

export interface WifiPoolItem {
  id: number;
  ssid: string;
  bssid: string;
  hitCount: number;
  lastSeenAt: string;
  lastStudentRkId?: number;
}

export interface AcceptedWifiItem {
  id: number;
  ssid: string;
  bssid: string;
  createdAt: string;
}

export type WifiAcceptItem = { ssid: string; bssid: string };

export const apiFetchWifiPool = async (q = '', limit = 50): Promise<{ data: WifiPoolItem[] }> => {
  const params = new URLSearchParams();
  if (q.trim()) params.set('q', q.trim());
  params.set('limit', String(limit));
  const query = params.toString() ? `?${params.toString()}` : '';
  const res = await staffFetch(`/wifi-pool${query}`);
  if (!res.ok) throw new Error('Failed to fetch wifi pool');
  return res.json();
};

export const apiFetchAcceptedWifis = async (): Promise<{ data: AcceptedWifiItem[] }> => {
  const res = await staffFetch('/network/accepted-wifis');
  if (!res.ok) throw new Error('Failed to fetch accepted wifis');
  return res.json();
};

export const apiSaveAcceptedWifis = async (items: WifiAcceptItem[]): Promise<any> => {
  const res = await staffFetch('/network/accepted-wifis', {
    method: 'POST',
    body: JSON.stringify({ items }),
  });
  if (!res.ok) throw new Error('Failed to save accepted wifis');
  return res.json();
};

export const apiFetchClassStudents = api.getClassStudents;
export const apiFetchClasses = api.getClasses;

export const apiFetchClassSessionLogs = async (
  rkId: number,
  date?: string,
  period?: number
): Promise<{ data: StudentSessionLogItem[]; period?: number; date?: string }> => {
  const params = new URLSearchParams();
  if (date) params.set('date', date);
  if (period) params.set('period', String(period));
  const query = params.toString() ? `?${params.toString()}` : '';
  const res = await staffFetch(`/classes/${rkId}/session-logs${query}`);
  if (!res.ok) throw new Error('Failed to fetch class session logs');
  return res.json();
};

export const apiFetchOnlineStudents = async (rkId: number): Promise<{ onlineStudentIds: number[] }> => {
  const res = await staffFetch(`/classes/${rkId}/online-students`);
  if (!res.ok) throw new Error('Failed to fetch online students list');
  return res.json();
};

export interface ScheduleConflictItem {
  studentRkId: number;
  fullName: string;
  studentCode: string;
  conflictClassRkId: number;
  conflictClassName: string;
  conflictClassCode: string;
}

export const apiFetchScheduleConflicts = async (rkId: number): Promise<{ conflicts: ScheduleConflictItem[]; ok: boolean }> => {
  const res = await staffFetch(`/classes/${rkId}/schedule-conflicts`);
  if (!res.ok) throw new Error('Failed to check schedule conflicts');
  return res.json();
};

export const apiFetchClassCourses = async (rkId: number): Promise<{
  data: ClassCourseItem[];
  source?: string;
  cached?: boolean;
  warning?: string;
  hint?: string;
}> => {
  const res = await staffFetch(`/classes/${rkId}/courses`);
  const body = await res.json().catch(() => ({}));
  if (!res.ok) {
    throw new Error(body.error || body.hint || 'Không tải được danh sách môn học từ QLĐT');
  }
  return body;
};

export const apiApplyScheduleTemplate = async (rkId: number): Promise<{ created: number; skipped: number }> => {
  const res = await staffFetch(`/classes/${rkId}/schedule/apply-template`, { method: 'POST' });
  if (!res.ok) throw new Error('Failed to apply schedule template');
  return res.json();
};

export const apiFetchAttendanceShifts = async (rkId: number, date?: string) => {
  const q = date ? `?date=${date}` : '';
  const res = await staffFetch(`/classes/${rkId}/attendance/shifts${q}`);
  if (!res.ok) throw new Error('Failed to fetch attendance shifts');
  return res.json();
};

export const apiFetchAttendance = async (rkId: number, date: string, period: number) => {
  const res = await staffFetch(`/classes/${rkId}/attendance?date=${date}&period=${period}`);
  if (!res.ok) throw new Error('Failed to fetch attendance');
  return res.json();
};

export const apiUpdateAttendanceStatus = async (
  rkId: number,
  payload: { date: string; period: number; studentRkId: number; status: number }
) => {
  const res = await staffFetch(`/classes/${rkId}/attendance/status`, {
    method: 'PUT',
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || 'Failed to update attendance status');
  }
  return res.json();
};

export const apiUpdateAttendanceBulkStatus = async (
  rkId: number,
  payload: { date: string; period: number; studentRkIds: number[]; status: number }
) => {
  const res = await staffFetch(`/classes/${rkId}/attendance/bulk-status`, {
    method: 'PUT',
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || 'Failed to update attendance status');
  }
  return res.json();
};

export const apiPushAttendanceQLDT = async (rkId: number, date: string, period: number) => {
  const res = await staffFetch(`/classes/${rkId}/attendance/push-qldt`, {
    method: 'POST',
    body: JSON.stringify({ date, period }),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || 'Failed to push attendance to QLĐT');
  }
  return res.json();
};

export interface LeaveRequestItem {
  id: number;
  date: string;
  note: string;
  reasonImage?: string;
  rejectReason?: string | null;
  period: number;
  status: string; // 'Đang chờ', 'Phê duyệt', 'Từ chối'
  approverId?: number | null;
  createdAt: string;
  student: {
    id: number;
    studentCode: string;
    fullName: string;
    phone?: string;
    email: string;
    avatar?: string;
  };
}

export const apiFetchLeaveRequests = async (
  classId: number,
  courseId: number,
  date: string
): Promise<LeaveRequestItem[]> => {
  const res = await staffFetch(`/classes/${classId}/leave-requests?courseId=${courseId}&date=${date}`);
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || 'Failed to fetch leave requests');
  }
  return res.json();
};

export const apiUpdateLeaveStatus = async (
  classId: number,
  leaveId: number,
  payload: { status: string; studentRkId: number; date: string; period: number }
): Promise<{ ok: boolean; message: string }> => {
  const res = await staffFetch(`/classes/${classId}/leave-requests/${leaveId}/status`, {
    method: 'POST',
    body: JSON.stringify(payload),
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || 'Failed to update leave status');
  }
  return res.json();
};

async function staffUpload(path: string, form: FormData): Promise<Response> {
  const headers = new Headers();
  const token = getToken();
  if (token) headers.set('Authorization', `Bearer ${token}`);
  return fetch(`${API_BASE}${path}`, { method: 'POST', headers, body: form });
}

export interface ExamRoomItem {
  id: number;
  name: string;
  startTime: string;
  endTime: string;
  allowedApps: string;
  quizUrl: string;
  gitRepoUrl?: string;
  gitBranch?: string;
  gitPublishUrl?: string;
  status: 'draft' | 'ready' | 'ended' | 'cancelled';
  studentCount: number;
  paperCount: number;
  displayStatus: 'draft' | 'ready' | 'active' | 'ended' | 'cancelled';
}

export interface ExamPaperResource {
  id: number;
  examPaperId: number;
  fileName: string;
}

export interface ExamPaper {
  id: number;
  examRoomId: number;
  title: string;
  pdfPath: string;
  sortOrder: number;
  resources: ExamPaperResource[];
}

export interface ExamRoomStudent {
  id: number;
  examRoomId: number;
  studentRkId: number;
  assignedPaperId?: number;
  paperSentAt?: string;
  paperScheduledAt?: string;
  fullName: string;
  studentCode: string;
  email: string;
  avatar?: string;
  paperTitle?: string;
  submitted: boolean;
}

export interface ExamSubmission {
  id: number;
  examRoomId: number;
  studentRkId: number;
  fileName: string;
  createdAt: string;
  fullName: string;
  studentCode: string;
  gitRepoUrl?: string;
  gitPublishUrl?: string;
}

function decodeBase64ToArrayBuffer(b64: string): ArrayBuffer {
  const bin = atob(b64);
  const out = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
  return out.buffer;
}

export const apiExam = {
  list: async (): Promise<{ data: ExamRoomItem[] }> => {
    const res = await staffFetch('/exam-rooms');
    if (!res.ok) await parseError(res, 'Failed');
    return res.json();
  },
  create: async (payload: { name: string; startTime: string; endTime: string; allowedApps?: string; quizUrl?: string }) => {
    const res = await staffFetch('/exam-rooms', { method: 'POST', body: JSON.stringify(payload) });
    if (!res.ok) await parseError(res, 'Tạo phòng thi thất bại');
    return res.json();
  },
  get: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}`);
    if (!res.ok) await parseError(res, 'Không tìm thấy');
    return res.json() as Promise<{
      room: ExamRoomItem;
      papers: ExamPaper[];
      students: ExamRoomStudent[];
      displayStatus: string;
      editable: boolean;
      prepEditable: boolean;
      canPublish: boolean;
      canUnpublish: boolean;
      canCancel: boolean;
    }>;
  },
  publish: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/publish`, { method: 'POST' });
    if (!res.ok) {
      const data = await res.json().catch(() => ({})) as { error?: string; issues?: string[] };
      if (data.issues?.length) {
        throw new Error(data.issues.join('\n'));
      }
      throw new Error(data.error || 'Đẩy phòng thi thất bại');
    }
    return res.json();
  },
  unpublish: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/unpublish`, { method: 'POST' });
    if (!res.ok) await parseError(res, 'Thu hồi thất bại');
    return res.json();
  },
  cancel: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/cancel`, { method: 'POST' });
    if (!res.ok) await parseError(res, 'Hủy phòng thi thất bại');
    return res.json();
  },
  update: async (id: number, payload: Partial<{ name: string; startTime: string; endTime: string; allowedApps: string; quizUrl: string }>) => {
    const res = await staffFetch(`/exam-rooms/${id}`, { method: 'PATCH', body: JSON.stringify(payload) });
    if (!res.ok) await parseError(res, 'Cập nhật thất bại');
    return res.json();
  },
  remove: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa thất bại');
    return res.json();
  },
  searchStudents: async (q: string) => {
    const res = await staffFetch(`/exam-rooms/search-students?q=${encodeURIComponent(q)}`);
    if (!res.ok) await parseError(res, 'Tìm kiếm thất bại');
    return res.json() as Promise<{ data: { studentRkId: number; fullName: string; studentCode: string; email: string; classCodes: string }[] }>;
  },
  addStudents: async (id: number, studentRkIds: number[]) => {
    const res = await staffFetch(`/exam-rooms/${id}/students`, { method: 'POST', body: JSON.stringify({ studentRkIds }) });
    if (!res.ok) await parseError(res, 'Thêm sinh viên thất bại');
    return res.json();
  },
  removeStudent: async (id: number, studentRkId: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/students/${studentRkId}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa sinh viên thất bại');
    return res.json();
  },
  fetchOnlineStudents: async (id: number): Promise<{ onlineStudentIds: number[] }> => {
    const res = await staffFetch(`/exam-rooms/${id}/online-students`);
    if (!res.ok) throw new Error('Không tải được trạng thái online');
    return res.json();
  },
  uploadPackage: async (id: number, title: string, pdf: File, resources: File[] = []) => {
    const form = new FormData();
    form.append('title', title);
    form.append('pdf', pdf);
    resources.forEach((f) => form.append('resources', f));
    const res = await staffUpload(`/exam-rooms/${id}/papers`, form);
    if (!res.ok) await parseError(res, 'Tạo gói đề thất bại');
    return res.json();
  },
  uploadPaper: async (id: number, title: string, pdf: File, resources: File[] = []) => {
    return apiExam.uploadPackage(id, title, pdf, resources);
  },
  uploadPackageResources: async (id: number, paperId: number, files: File[]) => {
    const form = new FormData();
    files.forEach((f) => form.append('resources', f));
    const res = await staffUpload(`/exam-rooms/${id}/papers/${paperId}/resources`, form);
    if (!res.ok) await parseError(res, 'Thêm tài nguyên thất bại');
    return res.json();
  },
  uploadResource: async (id: number, paperId: number, file: File) => {
    return apiExam.uploadPackageResources(id, paperId, [file]);
  },
  deletePaper: async (id: number, paperId: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/papers/${paperId}`, { method: 'DELETE' });
    if (!res.ok) await parseError(res, 'Xóa gói đề thất bại');
    return res.json();
  },
  fetchPaperPdfBytes: async (examId: number, paperId: number): Promise<ArrayBuffer> => {
    const res = await staffFetch(`/exam-rooms/${examId}/papers/${paperId}/view`);
    if (!res.ok) await parseError(res, 'Không mở được đề PDF');
    const json = await res.json() as { data: string };
    if (!json.data) throw new Error('Server không trả dữ liệu PDF');
    return decodeBase64ToArrayBuffer(json.data);
  },
  fetchPaperResourceBytes: async (examId: number, paperId: number, resourceId: number): Promise<ArrayBuffer> => {
    const res = await staffFetch(`/exam-rooms/${examId}/papers/${paperId}/resources/${resourceId}/view`);
    if (!res.ok) await parseError(res, 'Không tải tài nguyên');
    const json = await res.json() as { data: string };
    if (!json.data) throw new Error('Server không trả dữ liệu file');
    return decodeBase64ToArrayBuffer(json.data);
  },
  downloadPaperFile: async (examId: number, paperId: number, opts?: { resourceId?: number; fileName?: string }) => {
    const params = new URLSearchParams();
    if (opts?.resourceId) {
      params.set('kind', 'resource');
      params.set('fileId', String(opts.resourceId));
    } else {
      params.set('kind', 'pdf');
    }
    const res = await staffFetch(`/exam-rooms/${examId}/papers/${paperId}/download?${params}`);
    if (!res.ok) await parseError(res, 'Tải file thất bại');
    const buf = await res.arrayBuffer();
    const url = URL.createObjectURL(new Blob([buf]));
    const a = document.createElement('a');
    a.href = url;
    a.download = opts?.fileName || 'de-thi.pdf';
    a.click();
    URL.revokeObjectURL(url);
  },
  assignRandom: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/assign-random`, { method: 'POST' });
    if (!res.ok) await parseError(res, 'Chia gói đề thất bại');
    return res.json() as Promise<{ assigned: number; packages: number }>;
  },
  sendPapers: async (id: number, payload?: { scheduledAt?: string; studentRkIds?: number[] }) => {
    const res = await staffFetch(`/exam-rooms/${id}/send-papers`, { method: 'POST', body: JSON.stringify(payload || {}) });
    if (!res.ok) await parseError(res, 'Gửi gói đề thất bại');
    return res.json();
  },
  listSubmissions: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/submissions`);
    if (!res.ok) await parseError(res, 'Failed');
    return res.json() as Promise<{ data: ExamSubmission[] }>;
  },
  downloadSubmission: (id: number, subId: number) => `${API_BASE}/exam-rooms/${id}/submissions/${subId}/download`,
  downloadAllSubmissions: async (id: number, fallbackName: string) => {
    let res: Response;
    try {
      res = await staffFetch(`/exam-rooms/${id}/submissions/download-all`);
    } catch {
      throw new Error('Không kết nối được server (Failed to fetch). Kiểm tra server đang chạy và thử lại.');
    }
    if (!res.ok) await parseError(res, 'Tải bài nộp gộp thất bại');
    const blob = await res.blob();
    if (!blob.size) throw new Error('File ZIP trống — có thể bài nộp trên server bị thiếu file');
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${fallbackName.replace(/[<>:"/\\|?*]+/g, '_')}_bai_nop.zip`;
    a.click();
    URL.revokeObjectURL(url);
  },
  saveGitSettings: async (id: number, payload: { gitRepoUrl?: string; gitBranch?: string }) => {
    const res = await staffFetch(`/exam-rooms/${id}/git-settings`, { method: 'PATCH', body: JSON.stringify(payload) });
    if (!res.ok) await parseError(res, 'Lưu cấu hình Git thất bại');
    return res.json() as Promise<{ gitRepoUrl: string; gitBranch: string; gitPublishUrl?: string }>;
  },
  publishSubmissionsGit: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/submissions/publish-git`, { method: 'POST' });
    if (!res.ok) {
      const errData = await res.json().catch(() => ({} as { error?: string; warnings?: string[] }));
      const msg = errData.error || 'Đẩy lên Git thất bại';
      if (errData.warnings?.length) throw new Error(`${msg} — ${errData.warnings.join('; ')}`);
      throw new Error(msg);
    }
    return res.json() as Promise<{ ok: boolean; url: string; gitPublishUrl: string; openUrl?: string; message: string }>;
  },
  publishSubmissionsGitStudents: async (id: number) => {
    const res = await staffFetch(`/exam-rooms/${id}/submissions/publish-git-students`, { method: 'POST' });
    if (!res.ok) {
      const errData = await res.json().catch(() => ({} as { error?: string; warnings?: string[] }));
      const msg = errData.error || 'Đẩy repo từng SV thất bại';
      if (errData.warnings?.length) throw new Error(`${msg} — ${errData.warnings.join('; ')}`);
      throw new Error(msg);
    }
    return res.json() as Promise<{
      ok: boolean;
      published: number;
      openUrl?: string;
      message: string;
      warnings?: string[];
      studentRepos?: { studentRkId: number; studentCode: string; fullName: string; url: string }[];
    }>;
  },
};

export const apiQldt = {
  getToken: async (): Promise<{ token: string }> => {
    const res = await staffFetch('/qldt-token');
    if (!res.ok) await parseError(res, 'Không tải được token QLĐT');
    return res.json();
  },
  saveToken: async (token: string): Promise<{ ok: boolean; message: string }> => {
    const res = await staffFetch('/qldt-token', {
      method: 'POST',
      body: JSON.stringify({ token }),
    });
    if (!res.ok) await parseError(res, 'Lưu token thất bại');
    return res.json();
  },
};

