import { createSampleData } from './sampleData'
import type { AppStateV1, LearningGoal, LearningTask, LoadResult, Priority, TaskStatus } from './types'

export const STORAGE_KEY = 'learning-planner.state'

const priorities: Priority[] = ['low', 'medium', 'high']
const statuses: TaskStatus[] = ['todo', 'in-progress', 'done']

function isString(value: unknown): value is string {
  return typeof value === 'string'
}

function isGoal(value: unknown): value is LearningGoal {
  if (!value || typeof value !== 'object') return false
  const goal = value as Record<string, unknown>
  return ['id', 'title', 'description', 'category', 'targetDate', 'color', 'createdAt'].every((key) => isString(goal[key]))
}

function isTask(value: unknown): value is LearningTask {
  if (!value || typeof value !== 'object') return false
  const task = value as Record<string, unknown>
  return (
    ['id', 'goalId', 'title', 'notes', 'dueDate', 'createdAt'].every((key) => isString(task[key])) &&
    priorities.includes(task.priority as Priority) &&
    statuses.includes(task.status as TaskStatus) &&
    typeof task.estimatedMinutes === 'number' &&
    Number.isFinite(task.estimatedMinutes) &&
    task.estimatedMinutes >= 0
  )
}

export function parseState(value: unknown): AppStateV1 {
  if (!value || typeof value !== 'object') throw new Error('The backup must contain an object.')
  const state = value as Record<string, unknown>
  if (state.version !== 1) throw new Error('This backup version is not supported.')
  if (!Array.isArray(state.goals) || !state.goals.every(isGoal)) throw new Error('The backup contains invalid goals.')
  if (!Array.isArray(state.tasks) || !state.tasks.every(isTask)) throw new Error('The backup contains invalid tasks.')
  const goalIds = new Set(state.goals.map((goal) => goal.id))
  if (state.tasks.some((task) => !goalIds.has(task.goalId))) throw new Error('A task refers to a goal that does not exist.')
  return state as unknown as AppStateV1
}

export const storageAdapter = {
  load(storage: Pick<Storage, 'getItem'> = localStorage): LoadResult {
    try {
      const raw = storage.getItem(STORAGE_KEY)
      if (!raw) return { state: createSampleData() }
      return { state: parseState(JSON.parse(raw)) }
    } catch (error) {
      return {
        state: createSampleData(),
        warning: `Saved data could not be read. Sample data was restored. ${error instanceof Error ? error.message : ''}`.trim(),
      }
    }
  },

  save(state: AppStateV1, storage: Pick<Storage, 'setItem'> = localStorage): void {
    storage.setItem(STORAGE_KEY, JSON.stringify(parseState(state)))
  },

  export(state: AppStateV1): string {
    return JSON.stringify(parseState(state), null, 2)
  },

  import(raw: string): AppStateV1 {
    return parseState(JSON.parse(raw))
  },
}
