import { IFile } from "../models/file"
import { ISocket } from "../models/socket"
const packageInfo = require("../../package.json")
import Console from "../utils/console"

const fs = require("fs")

function createUUID(): string {
  return (
    Math.random().toString(36).slice(2, 10) +
    Math.random().toString(36).slice(2, 10)
  )
}

function stringToBase64(input: string): string {
  return Buffer.from(input).toString("base64")
}

type TCompilationAttempt = {
  source_code: string
  stdout: string
  exit_code: number
  starting_at: number
  ending_at: number
}

type TTestAttempt = {
  source_code: string
  stdout: string
  exit_code: number
  starting_at: number
  ending_at: number
}

type TAIInteraction = {
  student_message: string
  source_code: string
  ai_response: string
  starting_at: number
  ending_at: number
}

type TQuizSelection = {
  question: string
  answer: string
  isCorrect: boolean
}

type TQuizSubmission = {
  quiz_hash: string
  selections: TQuizSelection[]
  submitted_at: number
}

export type TStep = {
  slug: string
  position: number
  files: IFile[]
  is_testeable: boolean
  opened_at?: number // The time when the step was opened
  completed_at?: number // If the step has tests, the time when all the tests passed, else, the time when the user opens the next step
  compilations: TCompilationAttempt[] // Everytime the user tries to compile the code
  tests: TTestAttempt[] // Everytime the user tries to run the tests
  ai_interactions: TAIInteraction[] // Everytime the user interacts with the AI
  quiz_submissions: TQuizSubmission[] // Everytime the user submits a quiz
}

type TWorkoutSession = {
  started_at: number
  ended_at?: number
}

type TStudent = {
  token: string
  user_id: string
  email: string
}

export interface ITelemetryJSONSchema {
  telemetry_id?: string
  version: string
  user_id?: number | string
  slug: string
  agent?: string
  tutorial_started_at: number
  last_interaction_at: number
  steps: Array<TStep> // The steps should be the same as the exercise
  workout_session: TWorkoutSession[] // It start when the user starts Learnpack, if the last_interaction_at is available, it automatically fills with that
  // number and start another session
}

export type TStepEvent =
  | "compile"
  | "test"
  | "ai_interaction"
  | "open_step"
  | "quiz_submission"

export type TTelemetryUrls = {
  streaming?: string
  batch?: string
}

type TUser = {
  token: string
  id: string
  email: string
}

interface ITelemetryManagerCLI {
  configPath: string | null
  socket: ISocket | null
  version: string
  start: (path: string, socket: ISocket) => void
  registerStepEvent: (
    stepPosition: number,
    event: TStepEvent,
    data: any
  ) => void
  save: (telemetry: ITelemetryJSONSchema) => void
  retrieve: () => Promise<ITelemetryJSONSchema | null>
}

const TelemetryManager: ITelemetryManagerCLI = {
  version: packageInfo.version,
  socket: null,

  configPath: "",

  start: function (path, socket) {
    this.configPath = path
    this.socket = socket
  },

  registerStepEvent: function (stepPosition, event, data) {
    Console.debug(`Registering Telemetry Event ${event}`)

    this.socket?.emit(
      "telemetry_event",
      "ready",
      [`Registering telemetry event ${event}`],
      [],
      [],
      {
        stepPosition,
        event,
        data,
      }
    )
  },
  retrieve: function () {
    return new Promise((resolve, reject) => {
      fs.readFile(
        `${this.configPath}/telemetry.json`,
        "utf8",
        (err: any, data: any) => {
          if (err) {
            if (err.code === "ENOENT") {
              // File does not exist, resolve with undefined
              resolve(null)
            } else {
              reject(err)
            }
          } else {
            try {
              resolve(JSON.parse(data))
            } catch {
              resolve(null)
            }
          }
        }
      )
    })
  },

  save: function (telemetry: ITelemetryJSONSchema) {
    fs.writeFile(
      `${this.configPath}/telemetry.json`,
      JSON.stringify(telemetry),
      (err: any) => {
        if (err) 
throw err
      }
    )
  },
}

export default TelemetryManager
