// src/server/features/auth/auth.service.ts
// Business logic for authentication (hashing, JWT generation)

import { db } from '@serverShared/db/client';
import { employees } from '@serverShared/db/schema';
import { and, eq } from 'drizzle-orm';
import { logger } from '@serverShared/lib/logger';
import { AppError, ErrorCode } from '@serverShared/lib/errors';
import * as argon2 from 'argon2';
import { randomUUID } from 'node:crypto';
import { USER_ROLES } from '@shared/constants/roles';

// Types
export interface LoginInput {
  username: string;
  password: string;
}

export interface RegisterInput {
  username: string;
  password: string;
  email: string;
  name: string;
  position?: string;
  phone?: string;
}

export interface AuthResult {
  user: {
    id: string;
    username: string;
    fullName: string | null;
    email: string | null;
    role: string;
    position: string | null;
  };
}

export interface UserResult {
  id: string;
  username: string;
  fullName: string | null;
  email: string | null;
  role: string;
  position: string | null;
}

// Password hashing using argon2 (secure and modern)
export async function hashPassword(password: string): Promise<string> {
  return await argon2.hash(password, {
    type: argon2.argon2id,
    memoryCost: 65536, // 64 MB
    timeCost: 3,
    parallelism: 4,
  });
}

// Password verification using argon2
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
  try {
    return await argon2.verify(hash, password);
  } catch (error) {
    logger.error('Password verification error:', error);
    return false;
  }
}

// Login function
export async function login(input: LoginInput): Promise<AuthResult> {
  const { username, password } = input;

  // Find employee by username
  const employeeResult = await db
    .select({
      id: employees.id,
      username: employees.username,
      fullName: employees.fullName,
      passwordHash: employees.passwordHash,
      email: employees.email,
      role: employees.role,
      position: employees.position,
    })
    .from(employees)
    .where(eq(employees.username, username))
    .limit(1);

  if (!employeeResult || employeeResult.length === 0) {
    logger.warn(`Login attempt failed: User not found - ${username}`);
    const error = new AppError('Неверный логин или пароль', 401, ErrorCode.UNAUTHORIZED);
    logger.error(`Throwing AppError: ${error.message}, statusCode: ${error.statusCode}, code: ${error.code}`);
    throw error;
  }

  const employee = employeeResult[0];

  // Verify password
  if (!employee.passwordHash) {
    logger.warn(`Login attempt failed: User has no password hash - ${username}`);
    const error = new AppError('Неверный логин или пароль', 401, ErrorCode.UNAUTHORIZED);
    logger.error(`Throwing AppError: ${error.message}, statusCode: ${error.statusCode}, code: ${error.code}`);
    throw error;
  }

  const isValidPassword = await verifyPassword(password, employee.passwordHash);
  if (!isValidPassword) {
    logger.warn(`Login attempt failed: Invalid password - ${username}`);
    const error = new AppError('Неверный логин или пароль', 401, ErrorCode.UNAUTHORIZED);
    logger.error(`Throwing AppError: ${error.message}, statusCode: ${error.statusCode}, code: ${error.code}`);
    throw error;
  }

  logger.info(`User logged in successfully: ${username}`);

  return {
    user: {
      id: employee.id,
      username: employee.username!,
      fullName: employee.fullName,
      email: employee.email,
      role: employee.role!,
      position: employee.position,
    },
  };
}

// Guest login via system VIEWER account
export async function guestLogin(): Promise<AuthResult> {
  const existingGuest = await db
    .select({
      id: employees.id,
      username: employees.username,
      fullName: employees.fullName,
      email: employees.email,
      role: employees.role,
      position: employees.position,
    })
    .from(employees)
    .where(
      and(
        eq(employees.isSystem, true),
        eq(employees.role, USER_ROLES.VIEWER),
      )
    )
    .limit(1);

  if (existingGuest.length > 0) {
    const guest = existingGuest[0];
    logger.info(`[Guest Login] Using existing system guest account: ${guest.username}`);

    return {
      user: {
        id: guest.id,
        username: guest.username || 'guest_system',
        fullName: guest.fullName,
        email: guest.email,
        role: guest.role || USER_ROLES.VIEWER,
        position: guest.position,
      },
    };
  }

  const guestId = randomUUID();
  const now = new Date();

  await db.insert(employees).values({
    id: guestId,
    fullName: 'Гость (Система)',
    username: 'guest_system',
    role: USER_ROLES.VIEWER,
    isSystem: true,
    isFired: false,
    createdAt: now,
    updatedAt: now,
  });

  logger.info('[Guest Login] Created system guest account: guest_system');

  return {
    user: {
      id: guestId,
      username: 'guest_system',
      fullName: 'Гость (Система)',
      email: null,
      role: USER_ROLES.VIEWER,
      position: null,
    },
  };
}

// Register function
export async function register(input: RegisterInput): Promise<UserResult> {
  const { username, password, email, name, position = 'Staff', phone } = input;

  // Check if username already exists
  const existingEmployee = await db
    .select({ id: employees.id })
    .from(employees)
    .where(eq(employees.username, username))
    .limit(1);

  if (existingEmployee && existingEmployee.length > 0) {
    logger.warn(`Registration attempt failed: Username already exists - ${username}`);
    throw new AppError('Пользователь с таким именем уже существует', 409, ErrorCode.CONFLICT);
  }

  // Hash password
  const passwordHash = await hashPassword(password);

  // Generate ID
  const employeeId = randomUUID();
  const now = new Date();

  // Create employee record in unified table
  await db.insert(employees).values({
    id: employeeId,
    fullName: name,
    position,
    phone: phone || '',
    email,
    username,
    passwordHash,
    role: 'MANAGER',
    createdAt: now,
    updatedAt: now,
  });

  // Fetch complete employee record
  const employeeResult = await db
    .select({
      id: employees.id,
      username: employees.username,
      fullName: employees.fullName,
      email: employees.email,
      role: employees.role,
      position: employees.position,
    })
    .from(employees)
    .where(eq(employees.id, employeeId))
    .limit(1);

  const employee = employeeResult[0];

  logger.info(`New user registered: ${username}`);

  return {
    id: employee.id,
    username: employee.username!,
    fullName: employee.fullName,
    email: employee.email,
    role: employee.role!,
    position: employee.position,
  };
}
