import { error, redirect } from '@sveltejs/kit';

import * as taskCrud from '$lib/services/tasks';
import * as taskResultsCrud from '$lib/services/task_results';
import { getLoggedInUser } from '$features/auth/services/session_guards';
import * as workBooksCrud from '$features/workbooks/services/workbooks';
import {
  getWorkbooksByPlacement,
  getWorkBooksCreatedByUsers,
  getAvailableSolutionCategories,
  getSolutionCategoryMapByWorkbookId,
} from '$features/workbooks/services/workbooks';

import { Roles } from '$lib/types/user';
import type { TaskGrade, TaskResult } from '$lib/types/task';
import {
  WorkBookTab,
  type WorkBookTab as WorkBookTabType,
  WorkBookType,
} from '$features/workbooks/types/workbook';
import {
  type PlacementQuery,
  ALL_SOLUTION_CATEGORIES,
  type SelectedSolutionCategory,
  type SolutionCategory,
} from '$features/workbooks/types/workbook_placement';

import { isAdmin, canDelete } from '$lib/utils/authorship';
import { buildTaskIdsFromWorkbooks } from '$features/workbooks/utils/workbooks';
import {
  parseWorkBookTab,
  parseWorkBookGrade,
  parseWorkBookCategory,
} from '$features/workbooks/utils/workbook_url_params';
import { parseWorkBookId } from '$features/workbooks/utils/workbook';

import {
  BAD_REQUEST,
  FORBIDDEN,
  FOUND,
  NOT_FOUND,
  INTERNAL_SERVER_ERROR,
} from '$lib/constants/http-response-status-codes';

export async function load({ locals, url }) {
  const loggedInUser = await getLoggedInUser(locals, url);
  const params = url.searchParams;

  const tab = parseWorkBookTab(params);

  // CREATED_BY_USER tab is admin-only
  if (
    tab === WorkBookTab.CREATED_BY_USER &&
    (!loggedInUser || !isAdmin(loggedInUser.role as Roles))
  ) {
    redirect(FOUND, '/workbooks');
  }

  const selectedGrade = parseWorkBookGrade(params);
  const selectedCategory = parseWorkBookCategory(params);

  // All tab content is gated by {#if loggedInUser}, so anonymous users render
  // nothing. Skip the heavy workbook/category fetches and return a
  // display-equivalent empty shape (keys preserved for the page's $effect/$derived).
  if (!loggedInUser) {
    return {
      workbooks: [],
      availableCategories: [],
      solutionCategoryMap: new Map<number, SolutionCategory>(),
      tasksMapByIds: new Map<string, { grade: TaskGrade }>(),
      taskResultsByTaskId: new Map<string, TaskResult>(),
      loggedInUser: null,
      tab,
      selectedGrade,
      selectedCategory,
    };
  }

  const adminUser = isAdmin(loggedInUser.role as Roles);

  try {
    const [workbooks, availableCategories, solutionCategoryMap, taskResultsByTaskId] =
      await Promise.all([
        fetchWorkbooksByTab(tab, selectedGrade, selectedCategory, !!adminUser),
        tab === WorkBookTab.SOLUTION
          ? getAvailableSolutionCategories(!!adminUser)
          : Promise.resolve([]),
        tab === WorkBookTab.SOLUTION && selectedCategory === ALL_SOLUTION_CATEGORIES
          ? getSolutionCategoryMapByWorkbookId(!!adminUser)
          : Promise.resolve(new Map<number, SolutionCategory>()),
        taskResultsCrud.getTaskResultsOnlyResultExists(loggedInUser.id, true),
      ]);

    // Grade modes are only displayed on the CURRICULUM tab.
    // For other tabs the id list is empty so tasksMapByIds becomes an empty Map.
    const referencedTaskIds =
      tab === WorkBookTab.CURRICULUM ? buildTaskIdsFromWorkbooks(workbooks) : [];
    const referencedTasks = await taskCrud.getTasksWithSelectedTaskIds(referencedTaskIds);
    const tasksMapByIds = new Map(
      referencedTasks.map((task) => [task.task_id, { grade: task.grade }]),
    );

    return {
      workbooks,
      availableCategories,
      solutionCategoryMap,
      tasksMapByIds,
      taskResultsByTaskId,
      loggedInUser,
      tab,
      selectedGrade,
      selectedCategory,
    };
  } catch (e) {
    console.error('Failed to fetch workbooks, tasks or task results: ', e);
    error(
      INTERNAL_SERVER_ERROR,
      '問題もしくは回答の取得に失敗しました。しばらくしてから、もう一度試してください。',
    );
  }
}

export const actions = {
  delete: async ({ locals, request }) => {
    const url = new URL(request.url);
    const loggedInUser = await getLoggedInUser(locals, url);
    const slug = url.searchParams.get('slug');
    const workBookId = parseWorkBookId(slug as string);

    if (workBookId === null) {
      error(BAD_REQUEST, '不正な問題集idです。');
    }

    const workBook = await workBooksCrud.getWorkBook(workBookId);

    if (!workBook) {
      error(NOT_FOUND, `問題集id: ${workBookId} は見つかりませんでした。`);
    }
    if (loggedInUser && !canDelete(loggedInUser.id, workBook.authorId)) {
      error(FORBIDDEN, `問題集id: ${workBookId} にアクセスする権限がありません。`);
    }

    try {
      await workBooksCrud.deleteWorkBook(workBookId);
      console.log(`Deleted workbook ${workBookId} by user ${loggedInUser?.id}`);
    } catch (e) {
      console.error(`Failed to delete WorkBook with id ${workBookId}:`, e);
      error(
        INTERNAL_SERVER_ERROR,
        `問題集id: ${workBookId} の削除に失敗しました。しばらくしてから、もう一度試してください。`,
      );
    }
  },
};

function fetchWorkbooksByTab(
  tab: WorkBookTabType,
  grade: ReturnType<typeof parseWorkBookGrade>,
  category: ReturnType<typeof parseWorkBookCategory>,
  includeUnpublished: boolean,
) {
  if (tab === WorkBookTab.CREATED_BY_USER) {
    return getWorkBooksCreatedByUsers();
  }

  return getWorkbooksByPlacement(buildPlacementQuery(tab, grade, category), includeUnpublished);
}

function buildPlacementQuery(
  tab: WorkBookTabType,
  grade: ReturnType<typeof parseWorkBookGrade>,
  category: SelectedSolutionCategory,
): PlacementQuery {
  if (tab === WorkBookTab.CURRICULUM) {
    return { workBookType: WorkBookType.CURRICULUM, taskGrade: grade };
  }

  return { workBookType: WorkBookType.SOLUTION, solutionCategory: category };
}
