import bcrypt from 'bcrypt';
import { UserRepository } from '@/repositories/UserRepository';
import { createSessionToken } from '@/lib/auth/session';
import {
  validateRegister,
  validateLogin,
  validateProfileUpdate,
  type ProfileUpdateInput,
} from '@/lib/validators/userValidator';
import { ConflictError, UnauthorizedError, NotFoundError } from '@/lib/errors';
import type { User, UserRole } from '@/lib/types';

const BCRYPT_ROUNDS = 10;

export interface RegisterInput {
  name: string;
  email: string;
  password: string;
}

export interface LoginResult {
  user: User;
  token: string;
}

/**
 * 認証・ユーザー管理の業務ロジックを担うService。
 * パスワードはbcryptでハッシュ化し、平文保存しない。
 * セッションCookieの設定はRoute Handler層の責務（AuthServiceはトークン生成まで）。
 */
export class AuthService {
  constructor(private readonly userRepository: UserRepository) {}

  register(input: RegisterInput): User {
    validateRegister(input);
    const existing = this.userRepository.findByEmail(input.email);
    if (existing) {
      throw new ConflictError('このメールアドレスは既に使用されています');
    }
    const passwordHash = bcrypt.hashSync(input.password, BCRYPT_ROUNDS);
    return this.userRepository.create({
      name: input.name,
      email: input.email,
      passwordHash,
      role: 'member',
    });
  }

  login(email: string, password: string): LoginResult {
    validateLogin({ email, password });
    const user = this.userRepository.findByEmail(email);
    if (!user) {
      throw new UnauthorizedError(
        'メールアドレスまたはパスワードが正しくありません'
      );
    }
    if (user.status === 'inactive') {
      throw new UnauthorizedError('このアカウントは無効です');
    }
    if (
      !user.passwordHash ||
      !bcrypt.compareSync(password, user.passwordHash)
    ) {
      throw new UnauthorizedError(
        'メールアドレスまたはパスワードが正しくありません'
      );
    }
    return { user, token: createSessionToken(user.id) };
  }

  logout(): void {
    // セッションCookieの削除はRoute Handler層で行う
  }

  getCurrentUser(userId: number): User | null {
    return this.userRepository.findById(userId);
  }

  updateProfile(userId: number, input: ProfileUpdateInput): User {
    validateProfileUpdate(input);
    const user = this.userRepository.findById(userId);
    if (!user) {
      throw new NotFoundError('User', userId);
    }
    if (input.email && input.email !== user.email) {
      const existing = this.userRepository.findByEmail(input.email);
      if (existing) {
        throw new ConflictError('このメールアドレスは既に使用されています');
      }
    }
    const updated = this.userRepository.update(userId, {
      name: input.name,
      email: input.email,
      avatarUrl: input.avatarUrl,
      theme: input.theme,
      locale: input.locale,
    });
    if (!updated) {
      throw new NotFoundError('User', userId);
    }
    return updated;
  }

  /** テスト/初期データ用途: ロールを直接指定してユーザー作成 */
  createWithRole(input: RegisterInput, role: UserRole): User {
    validateRegister(input);
    const existing = this.userRepository.findByEmail(input.email);
    if (existing) {
      throw new ConflictError('このメールアドレスは既に使用されています');
    }
    const passwordHash = bcrypt.hashSync(input.password, BCRYPT_ROUNDS);
    return this.userRepository.create({
      name: input.name,
      email: input.email,
      passwordHash,
      role,
    });
  }
}
