import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { sql } from '../db.js';
import type { User, UserRole } from '@wa-ticketing/shared';
import { auditService } from './auditService.js';

const JWT_SECRET  = process.env.JWT_SECRET!;
const JWT_EXPIRY  = process.env.JWT_EXPIRY ?? '8h';
const SALT_ROUNDS = 12;

export const authService = {
  async login(email: string, password: string): Promise<{ token: string; user: User }> {
    const [row] = await sql<[User & { password_hash: string }]>`
      SELECT id, email, display_name, phone, password_hash, role, company_id, is_active, created_at, last_login_at
      FROM users
      WHERE email = ${email.toLowerCase()} AND is_active = TRUE
    `;

    if (!row) {
      await auditService.log(null, 'system', 'user.login_failed', 'user', '00000000-0000-0000-0000-000000000000', { email });
      throw Object.assign(new Error('Invalid credentials'), { statusCode: 401 });
    }

    const valid = await bcrypt.compare(password, row.password_hash);
    if (!valid) {
      await auditService.log(null, 'system', 'user.login_failed', 'user', row.id, { email });
      throw Object.assign(new Error('Invalid credentials'), { statusCode: 401 });
    }

    await sql`UPDATE users SET last_login_at = NOW() WHERE id = ${row.id}`;
    await auditService.log(row.id, 'user', 'user.login', 'user', row.id, {});

    const token = jwt.sign(
      {
        sub: row.id,
        email: row.email,
        role: row.role,
        company_id: row.company_id ?? null,
      },
      JWT_SECRET,
      { expiresIn: JWT_EXPIRY } as jwt.SignOptions,
    );

    const { password_hash: _, ...user } = row;
    return { token, user };
  },

  /** Staff accounts (admin / agent) — never tied to a company */
  async createUser(email: string, password: string, role: 'admin' | 'user'): Promise<User> {
    const hash = await bcrypt.hash(password, SALT_ROUNDS);
    const [user] = await sql<[User]>`
      INSERT INTO users (email, password_hash, role, phone)
      VALUES (${email.toLowerCase()}, ${hash}, ${role}, NULL)
      RETURNING id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
    `;
    return user;
  },

  async createCompanyPortalUser(
    email: string,
    password: string,
    companyId: string,
    displayName?: string | null,
    phone?: string | null,
  ): Promise<User> {
    const hash = await bcrypt.hash(password, SALT_ROUNDS);
    const dn = displayName?.trim() ? displayName.trim() : null;
    const ph = phone?.trim() ? phone.trim() : null;
    const [user] = await sql<[User]>`
      INSERT INTO users (email, password_hash, role, company_id, display_name, phone)
      VALUES (${email.toLowerCase()}, ${hash}, 'company_user', ${companyId}, ${dn}, ${ph})
      RETURNING id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
    `;
    return user;
  },

  async getUser(id: string): Promise<User | null> {
    const [row] = await sql<[User]>`
      SELECT id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
      FROM users WHERE id = ${id}
    `;
    return row ?? null;
  },

  async listUsers(): Promise<User[]> {
    return sql<User[]>`
      SELECT id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
      FROM users ORDER BY created_at DESC
    `;
  },

  async updateUser(id: string, patch: { role?: UserRole; is_active?: boolean }): Promise<User> {
    if (patch.role !== undefined && patch.is_active !== undefined) {
      const [user] = await sql<[User]>`
        UPDATE users SET role = ${patch.role}, is_active = ${patch.is_active}
        WHERE id = ${id}
        RETURNING id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
      `;
      return user;
    }
    if (patch.role !== undefined) {
      const [user] = await sql<[User]>`
        UPDATE users SET role = ${patch.role}
        WHERE id = ${id}
        RETURNING id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
      `;
      return user;
    }
    if (patch.is_active !== undefined) {
      const [user] = await sql<[User]>`
        UPDATE users SET is_active = ${patch.is_active}
        WHERE id = ${id}
        RETURNING id, email, display_name, phone, role, company_id, is_active, created_at, last_login_at
      `;
      return user;
    }
    throw new Error('Nothing to update');
  },

  async deactivateUser(id: string): Promise<void> {
    await sql`UPDATE users SET is_active = FALSE WHERE id = ${id}`;
  },

  async changePassword(id: string, newPassword: string): Promise<void> {
    const hash = await bcrypt.hash(newPassword, SALT_ROUNDS);
    await sql`UPDATE users SET password_hash = ${hash} WHERE id = ${id}`;
  },
};
