import type { BookmarkItem, CbtSessionRecord, CbtStorageState, ProgressStat, QuestionRecord, WrongNoteItem } from './cbtTypes';

const STORAGE_KEY = 'play-cbt-storage-v1';

function getDefaultState(): CbtStorageState {
  return {
    sessions: [],
    wrongNotes: [],
    bookmarks: [],
    progressStats: [],
    activeSessionId: null,
  };
}

export function loadCbtStorageState(): CbtStorageState {
  if (typeof window === 'undefined') {
    return getDefaultState();
  }

  try {
    const raw = window.localStorage.getItem(STORAGE_KEY);

    if (!raw) {
      return getDefaultState();
    }

    const parsed = JSON.parse(raw) as Partial<CbtStorageState>;

    return {
      sessions: Array.isArray(parsed.sessions) ? parsed.sessions : [],
      wrongNotes: Array.isArray(parsed.wrongNotes) ? parsed.wrongNotes : [],
      bookmarks: Array.isArray(parsed.bookmarks) ? parsed.bookmarks : [],
      progressStats: Array.isArray(parsed.progressStats) ? parsed.progressStats : [],
      activeSessionId: typeof parsed.activeSessionId === 'string' ? parsed.activeSessionId : null,
    };
  } catch {
    return getDefaultState();
  }
}

export function saveCbtStorageState(state: CbtStorageState) {
  if (typeof window === 'undefined') {
    return;
  }

  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}

export function upsertSession(sessions: CbtSessionRecord[], session: CbtSessionRecord) {
  const nextSessions = sessions.filter((item) => item.id !== session.id);
  nextSessions.unshift(session);
  return nextSessions.slice(0, 40);
}

export function mergeWrongNotes(
  previous: WrongNoteItem[],
  session: CbtSessionRecord,
  questionMap: Map<string, QuestionRecord>,
) {
  const next = new Map(previous.map((item) => [item.questionId, item]));

  session.answers.forEach((answer) => {
    const question = questionMap.get(answer.questionId);

    if (!question || !answer.selectedValue || answer.selectedValue === question.answerValue) {
      return;
    }

    const current = next.get(answer.questionId);
    next.set(answer.questionId, {
      questionId: answer.questionId,
      createdAt: current?.createdAt ?? session.submittedAt ?? session.startedAt,
      subjectId: question.subjectId,
      examId: question.examId,
      wrongCount: (current?.wrongCount ?? 0) + 1,
      lastSessionId: session.id,
    });
  });

  return Array.from(next.values()).sort((left, right) => right.createdAt.localeCompare(left.createdAt));
}

export function mergeBookmarks(previous: BookmarkItem[], session: CbtSessionRecord) {
  const next = new Map(previous.map((item) => [item.questionId, item]));

  session.answers.forEach((answer) => {
    const current = next.get(answer.questionId);

    if (answer.isBookmarked) {
      const timestamp = answer.answeredAt ?? session.submittedAt ?? session.startedAt;
      next.set(answer.questionId, {
        questionId: answer.questionId,
        createdAt: current?.createdAt ?? timestamp,
        updatedAt: timestamp,
      });
      return;
    }

    if (current) {
      next.delete(answer.questionId);
    }
  });

  return Array.from(next.values()).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
}

export function buildProgressStats(sessions: CbtSessionRecord[], questionMap: Map<string, QuestionRecord>): ProgressStat[] {
  const subjectMap = new Map<string, ProgressStat>();

  sessions
    .filter((session) => session.submittedAt)
    .forEach((session) => {
      session.answers.forEach((answer) => {
        if (!answer.selectedValue) {
          return;
        }

        const question = questionMap.get(answer.questionId);

        if (!question) {
          return;
        }

        const current = subjectMap.get(question.subjectId) ?? {
          subjectId: question.subjectId,
          solvedCount: 0,
          correctCount: 0,
          wrongCount: 0,
          accuracyRate: 0,
        };

        current.solvedCount += 1;

        if (answer.selectedValue === question.answerValue) {
          current.correctCount += 1;
        } else {
          current.wrongCount += 1;
        }

        current.accuracyRate = current.solvedCount > 0 ? Math.round((current.correctCount / current.solvedCount) * 100) : 0;
        subjectMap.set(question.subjectId, current);
      });
    });

  return Array.from(subjectMap.values()).sort((left, right) => right.solvedCount - left.solvedCount);
}
