import type { Static } from "@sinclair/typebox"
import type { Document } from "mongoose"
import type { PluginFastifyInstance } from "types"
import { Type } from "@sinclair/typebox"

/**
 * Base User Schema - Fields common to all operations
 */
const baseUserSchema = {
  email: Type.String({ format: "email" }),
  title: Type.Optional(Type.String()),
  firstname: Type.String(),
  lastname: Type.String(),
  email_verified: Type.Boolean({ default: false }),
  active: Type.Boolean({ default: true }),
  blocked: Type.Boolean({ default: false }),
  tags: Type.Array(Type.String(), { default: [] }),
  // TODO: below must be defined better, but also dynamically expandable
  settings: Type.Optional(Type.Any()),
  ux: Type.Optional(Type.Any()),
}

/**
 * Full User Schema - Used for response serialization and database model
 */
export const userSchema = Type.Object(
  {
    _id: Type.String({ format: "uuid" }),
    ...baseUserSchema,
    password: Type.Optional(Type.String()),
    owner: Type.Optional(Type.String()),
    account: Type.Optional(Type.String()),
    resetToken: Type.Optional(Type.String()),
    resetDate: Type.Optional(Type.String({ format: "date-time" })),
    passwordUpdatedAt: Type.Optional(Type.String({ format: "date-time" })),
    signedTermsAt: Type.Optional(Type.String({ format: "date-time" })),
    createdAt: Type.String({ format: "date-time" }),
    updatedAt: Type.String({ format: "date-time" }),
    isDeleted: Type.Boolean({ default: false }),
  },
  {
    $id: "#user/base",
    title: "core/user",
  },
)

/**
 * User Create Schema - For POST operations
 */
export const userCreateSchema = Type.Object(
  {
    _id: Type.Optional(Type.String()),
    ...baseUserSchema,
    password: Type.String(),
    account: Type.String(),
    active: Type.Optional(Type.Boolean({ default: true })),
    blocked: Type.Optional(Type.Boolean({ default: false })),
  },
  {
    $id: "#user/create",
    title: "core/user-create",
  },
)

/**
 * User Update Schema - For PATCH operations
 */
export const userUpdateSchema = Type.Object(
  {
    _id: Type.Optional(Type.String()),
    email: Type.Optional(Type.String({ format: "email" })),
    title: Type.Optional(Type.String()),
    firstname: Type.Optional(Type.String()),
    lastname: Type.Optional(Type.String()),
    email_verified: Type.Optional(Type.Boolean()),
    active: Type.Optional(Type.Boolean()),
    blocked: Type.Optional(Type.Boolean()),
    tags: Type.Optional(Type.Array(Type.String())),
    password: Type.Optional(Type.String()),
    settings: Type.Optional(Type.Any()),
    ux: Type.Optional(Type.Any()),
  },
  {
    $id: "#user/update",
    title: "core/user-update",
  },
)

/**
 * Public User Schema - For external API responses
 */
export const PublicUserSchema = Type.Omit(userSchema, [
  "password",
  "resetToken",
  "resetDate",
])

/**
 * Type Utilities
 */

type DateTimeFields = {
  resetDate?: Date
  passwordUpdatedAt?: Date
  signedTermsAt?: Date
  createdAt: Date
  updatedAt: Date
}

// in the user.model.ts we bloat some methods on the Model
export interface IUserMethods {
  hashPassword(password: string): Promise<string | undefined>
  checkPassword(guess: string): Promise<boolean>
  setResetToken(): string
}

type RawUser = Static<typeof userSchema>
export type User = Omit<RawUser, keyof DateTimeFields> & DateTimeFields

type RawUserCreate = Static<typeof userCreateSchema>
export type UserCreate = RawUserCreate

type RawUserUpdate = Static<typeof userUpdateSchema>
export type UserUpdate = RawUserUpdate

export interface UserDoc extends Document, User, IUserMethods {
  _id: string
}

export type RawPublicUser = Static<typeof PublicUserSchema>
export type PublicUser = Omit<RawPublicUser, keyof DateTimeFields> &
  DateTimeFields

export type AnyUser = User | UserDoc

// Helper function to convert User to PublicUser
export const toPublicUser = (user: User | UserDoc): PublicUser => {
  const isUserDoc = (user: User | UserDoc): user is UserDoc => {
    return "toObject" in user
  }

  // Use the object directly or call toObject based on type
  const userObj = isUserDoc(user) ? user.toObject<User>() : user

  delete userObj.password
  delete userObj.resetToken
  delete userObj.resetDate

  return userObj
}

/**
 * Register Schema/Type Generation for Shared Folder
 */
export const registerSchemas = (fastify: PluginFastifyInstance) => {
  fastify.registerSchema({
    schema: userSchema,
    publicSchema: PublicUserSchema,
    name: "User",
  })

  fastify.registerSchema({
    schema: userCreateSchema,
    name: "UserCreate",
  })

  fastify.registerSchema({
    schema: userUpdateSchema,
    name: "UserUpdate",
  })
}
