<?php
// includes/auth.php
declare(strict_types=1);

require_once __DIR__ . '/db.php';

function ensure_session(): void {
  if (session_status() !== PHP_SESSION_ACTIVE) session_start();
}

function current_user(): ?array {
  ensure_session();
  return $_SESSION['user'] ?? null;
}

function require_login(): void {
  ensure_session();
  if (empty($_SESSION['user'])) {
    header('Location: login.php');
    exit;
  }
}

/** Lee roles del usuario desde BD */
function user_roles_from_db(int $userId): array {
  $pdo = pdo();
  $st = $pdo->prepare("
    SELECT r.code
    FROM user_roles ur
    JOIN roles r ON r.id = ur.role_id
    WHERE ur.user_id = ?
  ");
  $st->execute([$userId]);

  $rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
  return array_values(array_map(
    static fn(array $x): string => (string)($x['code'] ?? ''),
    $rows
  ));
}

/** Devuelve roles actuales (prioriza sesión) */
function user_roles(int $userId): array {
  $u = current_user();
  if ($u && (int)($u['id'] ?? 0) === $userId) {
    return array_values(array_unique(array_map('strval', (array)($u['roles'] ?? []))));
  }
  return user_roles_from_db($userId);
}

function has_role(string $role): bool {
  $u = current_user();
  if (!$u) return false;
  return in_array($role, (array)($u['roles'] ?? []), true);
}

function has_any_role(array $roles): bool {
  $u = current_user();
  if (!$u) return false;

  $userRoles = array_values(array_unique(array_map('strval', (array)($u['roles'] ?? []))));
  foreach ($roles as $role) {
    if (in_array((string)$role, $userRoles, true)) {
      return true;
    }
  }
  return false;
}

function require_any_role(array $roles): void {
  require_login();
  if (!has_any_role($roles)) {
    http_response_code(403);
    exit('Acceso denegado.');
  }
}

function is_global_user(int $userId): bool {
  // admin/corp ven todo
  $roles = user_roles($userId);
  return in_array('admin', $roles, true) || in_array('corp', $roles, true);
}

/* ========= Permisos por módulo ========= */

function module_role_map(): array {
  return [
    'produccion'            => ['admin', 'corp', 'plant_admin', 'production'],
    'produccion_cubicado'   => ['admin', 'corp', 'plant_admin', 'production'],
    'calidad'               => ['admin', 'corp', 'plant_admin', 'quality'],
    'ventas'                => ['admin', 'corp', 'plant_admin', 'ventas'],
    'almacen'               => ['admin', 'corp', 'plant_admin', 'warehouse', 'ventas'],
    'transferencias'        => ['admin', 'corp', 'plant_admin', 'warehouse'],
    'reportes'              => ['admin', 'corp', 'plant_admin'],
    'admin'                 => ['admin'],
  ];
}

function user_allowed_modules(?int $userId = null): array {
  $u = current_user();
  if (!$u) return [];

  $uid = $userId ?? (int)($u['id'] ?? 0);
  if ($uid <= 0) return [];

  $roles = user_roles($uid);

  if (in_array('admin', $roles, true) || in_array('corp', $roles, true)) {
    return array_keys(module_role_map());
  }

  $allowed = [];
  foreach (module_role_map() as $module => $moduleRoles) {
    foreach ($moduleRoles as $role) {
      if (in_array($role, $roles, true)) {
        $allowed[] = $module;
        break;
      }
    }
  }

  return array_values(array_unique($allowed));
}

function can_access_module(string $module, ?int $userId = null): bool {
  return in_array($module, user_allowed_modules($userId), true);
}

function require_module(string $module): void {
  require_login();

  $u = current_user();
  $userId = (int)($u['id'] ?? 0);

  if ($userId <= 0 || !can_access_module($module, $userId)) {
    http_response_code(403);
    exit('Acceso denegado.');
  }
}

function login(string $username, string $password): bool {
  ensure_session();
  $pdo = pdo();

  $username = trim($username);
  if ($username === '' || $password === '') return false;

  $st = $pdo->prepare("
    SELECT id, username, full_name, email, pass_hash, active
    FROM users
    WHERE username=?
    LIMIT 1
  ");
  $st->execute([$username]);
  $row = $st->fetch(PDO::FETCH_ASSOC);

  if (!$row || (int)$row['active'] !== 1) return false;
  if (!password_verify($password, (string)$row['pass_hash'])) return false;

  if (password_needs_rehash((string)$row['pass_hash'], PASSWORD_DEFAULT)) {
    $newHash = password_hash($password, PASSWORD_DEFAULT);
    $up = $pdo->prepare("UPDATE users SET pass_hash=?, updated_at=NOW() WHERE id=?");
    $up->execute([$newHash, (int)$row['id']]);
  }

  $roles = user_roles_from_db((int)$row['id']);

  session_regenerate_id(true);

  $_SESSION['user'] = [
    'id'        => (int)$row['id'],
    'username'  => (string)$row['username'],
    'full_name' => (string)$row['full_name'],
    'email'     => (string)($row['email'] ?? ''),
    'roles'     => $roles,
  ];

  unset($_SESSION['scope']);
  return true;
}

function logout(): void {
  ensure_session();
  $_SESSION = [];

  if (ini_get('session.use_cookies')) {
    $p = session_get_cookie_params();
    setcookie(
      session_name(),
      '',
      time() - 42000,
      $p['path'],
      $p['domain'],
      $p['secure'],
      $p['httponly']
    );
  }

  session_destroy();
}

function scope(): ?array {
  ensure_session();
  return $_SESSION['scope'] ?? null;
}

function set_scope(int $companyId, int $locationId): void {
  ensure_session();
  $_SESSION['scope'] = [
    'company_id' => $companyId,
    'location_id' => $locationId
  ];
}

function require_scope(): void {
  require_login();
  if (!scope()) {
    header('Location: select_scope.php');
    exit;
  }
}

/* ========= Accesos por usuario (empresas/localidades) ========= */

function allowed_companies(int $userId): array {
  $pdo = pdo();

  if (is_global_user($userId)) {
    $st = $pdo->query("SELECT id, name, code FROM companies WHERE active=1 ORDER BY name");
    return $st->fetchAll(PDO::FETCH_ASSOC);
  }

  $st = $pdo->prepare("
    SELECT c.id, c.name, c.code
    FROM user_company_access uca
    JOIN companies c ON c.id = uca.company_id
    WHERE uca.user_id = ? AND c.active=1
    ORDER BY c.name
  ");
  $st->execute([$userId]);
  return $st->fetchAll(PDO::FETCH_ASSOC);
}

function allowed_locations(int $userId, int $companyId): array {
  $pdo = pdo();

  if (is_global_user($userId)) {
    $st = $pdo->prepare("
      SELECT id, name, code
      FROM locations
      WHERE company_id=? AND active=1
      ORDER BY name
    ");
    $st->execute([$companyId]);
    return $st->fetchAll(PDO::FETCH_ASSOC);
  }

  $st = $pdo->prepare("
    SELECT l.id, l.name, l.code
    FROM user_location_access ula
    JOIN locations l ON l.id = ula.location_id
    WHERE ula.user_id = ? AND l.company_id=? AND l.active=1
    ORDER BY l.name
  ");
  $st->execute([$userId, $companyId]);
  return $st->fetchAll(PDO::FETCH_ASSOC);
}

/* ========= Admin helpers ========= */

function set_user_roles(int $userId, array $roleCodes): void {
  $pdo = pdo();
  $roleCodes = array_values(array_unique(array_filter(array_map('trim', $roleCodes))));

  $pdo->beginTransaction();
  try {
    $pdo->prepare("DELETE FROM user_roles WHERE user_id=?")->execute([$userId]);

    if ($roleCodes) {
      $in = implode(',', array_fill(0, count($roleCodes), '?'));
      $st = $pdo->prepare("SELECT id, code FROM roles WHERE code IN ($in)");
      $st->execute($roleCodes);
      $rows = $st->fetchAll(PDO::FETCH_ASSOC);

      $ins = $pdo->prepare("INSERT INTO user_roles(user_id, role_id) VALUES(?,?)");
      foreach ($rows as $r) {
        $ins->execute([$userId, (int)$r['id']]);
      }
    }

    $pdo->commit();
  } catch (\Throwable $e) {
    $pdo->rollBack();
    throw $e;
  }
}

function set_user_company_access(int $userId, array $companyIds): void {
  $pdo = pdo();

  $pdo->beginTransaction();
  try {
    $pdo->prepare("DELETE FROM user_company_access WHERE user_id=?")->execute([$userId]);

    $ins = $pdo->prepare("INSERT INTO user_company_access(user_id, company_id) VALUES(?,?)");
    foreach ($companyIds as $cid) {
      $cid = (int)$cid;
      if ($cid > 0) {
        $ins->execute([$userId, $cid]);
      }
    }

    $pdo->commit();
  } catch (\Throwable $e) {
    $pdo->rollBack();
    throw $e;
  }
}

function set_user_location_access(int $userId, array $locationIds): void {
  $pdo = pdo();

  $pdo->beginTransaction();
  try {
    $pdo->prepare("DELETE FROM user_location_access WHERE user_id=?")->execute([$userId]);

    $ins = $pdo->prepare("INSERT INTO user_location_access(user_id, location_id) VALUES(?,?)");
    foreach ($locationIds as $lid) {
      $lid = (int)$lid;
      if ($lid > 0) {
        $ins->execute([$userId, $lid]);
      }
    }

    $pdo->commit();
  } catch (\Throwable $e) {
    $pdo->rollBack();
    throw $e;
  }
}