import type { RowDataPacket, ResultSetHeader } from 'mysql2'
import { randomBytes, randomUUID } from 'crypto'
import bcrypt from 'bcryptjs'
import { getPool } from '@/lib/db'

export type DbUser = {
  id: string
  email: string
  emailVerified: Date | null
  name: string | null
  password: string
  termsAcceptedAt: Date
  createdAt: Date
  updatedAt: Date
}

type UserRow = RowDataPacket & {
  id: string
  email: string
  email_verified: Date | null
  name: string | null
  password: string
  terms_accepted_at: Date
  created_at: Date
  updated_at: Date
}

function mapRow(row: UserRow): DbUser {
  return {
    id: row.id,
    email: row.email,
    emailVerified: row.email_verified,
    name: row.name,
    password: row.password,
    termsAcceptedAt: row.terms_accepted_at,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  }
}

export async function findUserByEmail(email: string): Promise<DbUser | null> {
  const normalized = email.trim().toLowerCase()
  const [rows] = await getPool().query<UserRow[]>(
    `SELECT id, email, email_verified, name, password, terms_accepted_at, created_at, updated_at
     FROM users WHERE email = ? LIMIT 1`,
    [normalized],
  )
  const row = rows[0]
  return row ? mapRow(row) : null
}

export async function createUser(input: {
  email: string
  passwordHash: string
  name: string | null
  termsAcceptedAt: Date
}): Promise<{ id: string }> {
  const id = randomUUID()
  const email = input.email.trim().toLowerCase()

  const [result] = await getPool().execute<ResultSetHeader>(
    `INSERT INTO users (id, email, email_verified, name, password, terms_accepted_at)
     VALUES (?, ?, NULL, ?, ?, ?)`,
    [id, email, input.name, input.passwordHash, input.termsAcceptedAt],
  )

  if (result.affectedRows !== 1) {
    throw new Error('Nie udało się zapisać użytkownika.')
  }

  return { id }
}

export async function emailExists(email: string): Promise<boolean> {
  const normalized = email.trim().toLowerCase()
  const [rows] = await getPool().query<RowDataPacket[]>(
    'SELECT 1 AS ok FROM users WHERE email = ? LIMIT 1',
    [normalized],
  )
  return rows.length > 0
}

/** Konto przy pierwszym logowaniu OAuth (hasło — losowe, nieużywane przy credentials). */
export async function ensureOAuthUser(emailRaw: string, name: string | null): Promise<{ id: string }> {
  const normalized = emailRaw.trim().toLowerCase()
  const existing = await findUserByEmail(normalized)
  if (existing) return { id: existing.id }

  const randomPwd = randomBytes(32).toString('hex')
  const passwordHash = await bcrypt.hash(randomPwd, 10)

  return createUser({
    email: normalized,
    passwordHash,
    name,
    termsAcceptedAt: new Date(),
  })
}
