<?php

namespace App\Services;

use App\Repositories\UserRepo;
use App\Interfaces\UserRequest;
use App\Models\UserModel;
use App\Models\UserRole;
use App\Services\TeknisiService;

class UserService
{
  private UserRepo $userRepo;
  private TeknisiService $teknisiService;

  public function __construct()
  {
    $this->userRepo = new UserRepo();
    $this->teknisiService = new TeknisiService();
  }

  public function findAll(): array
  {
    return $this->userRepo->findAll();
  }

  public function findBy(?string $keyword, ?string $role, ?string $divisi, ?string $statusAkun): array
  {
    return $this->userRepo->findBy($keyword, $role, $divisi, $statusAkun);
  }

  public function findById(int $id): ?UserModel
  {
    return $this->userRepo->findById($id);
  }

  public function save(UserRequest $request): void
  {
    // validation
    $this->isValid($request);
    $this->isValidPassword($request->getPassword());
    $request->hashPassword();
    $this->userRepo->save($request);

    if ($request->getRole() === UserRole::TEKNISI) {
      $user = $this->userRepo->getByUsername($request->getUsername());
      $this->teknisiService->save($user->getId(), $request->getSpesialisasi(), $request->getStatusAkun()->value);
    }
  }

  public function update(UserRequest $request): void
  {
    // get user
    $user = $this->userRepo->findById($request->getId());

    if ($user === null) {
      throw new \Exception('User tidak ditemukan.');
    }

    // validation
    $this->isValid($request);

    // check duplication on update
    $this->checkDuplicationOnUpdate($request->getUsername(), $request->getEmail(), $user->getId());

    $this->userRepo->update($request);

    if ($request->getRole() === UserRole::TEKNISI) {
      $teknisi = $this->teknisiService->findByUserId($request->getId());
      $this->teknisiService->update($teknisi->getId(), $request->getSpesialisasi(), $request->getStatusAkun()->value);
    }
  }

  public function changePassword(string $id, string $password, string $confirmPassword): void
  {
    // validation
    if ($password !== $confirmPassword) {
      throw new \Exception('Password tidak sama.');
    }
    $hashPw = password_hash($password, PASSWORD_DEFAULT);
    $this->userRepo->changePassword($id, $hashPw);
  }

  // validation
  private function validate(UserRequest $request): array
  {
    $errors = [];

    if (trim($request->getNama()) === '') {
      $errors['nama'] = 'Nama wajib diisi.';
    }

    if (trim($request->getUsername()) === '') {
      $errors['username'] = 'Username wajib diisi.';
    }

    if (strlen($request->getUsername()) < 4) {
      $errors['username'] = 'Username minimal 4 karakter.';
    }

    if (!filter_var($request->getEmail(), FILTER_VALIDATE_EMAIL)) {
      $errors['email'] = 'Format email tidak valid.';
    }

    if (!preg_match('/^08[0-9]{8,13}$/', $request->getNoTelepon())) {
      $errors['noTelepon'] = 'Nomor telepon tidak valid.';
    }

    if ($request->getRole() === UserRole::TEKNISI && ($request->getDivisiId() === null || $request->getDivisiId() === 0)) {
      $errors['divisiId'] = 'Divisi wajib dipilih untuk teknisi.';
    }

    return $errors;
  }

  private function isValid(UserRequest $request)
  {
    $err = $this->validate($request);
    if (!empty($err)) {
      throw new \Exception(implode(', ', $err));
    }
  }

  private function isValidPassword(string $password)
  {
    $errors = [];
    if (trim($password) === '') {
      $errors['password'] = 'Password wajib diisi.';
    }

    if (strlen($password) < 6) {
      $errors['password'] = 'Password minimal 6 karakter.';
    }

    if (!empty($errors)) {
      throw new \Exception(implode(', ', $errors));
    }
  }

  private function checkDuplicationOnUpdate(string $username, string $email, int $id)
  {
    $user = $this->userRepo->getByUsername($username);
    if ($user && $user->getId() !== $id) {
      throw new \Exception('User sudah ada');
    }

    $user = $this->userRepo->getByEmail($email);
    if ($user && $user->getId() !== $id) {
      throw new \Exception('Email sudah ada');
    }
  }
}
