/**
 * Roles & permissions admin (legacy CI rolepermission/*).
 * Live Laravel routes are exposed under `/api/v1/role-permissions`.
 */
import { ApiHttpError, apiFetch } from './http'

const BASE = '/api/v1/role-permissions'

type DataEnvelope<T> = { data?: T }

function unwrapData<T>(body: T | DataEnvelope<T>): T {
  if (body && typeof body === 'object' && 'data' in body) {
    const data = (body as DataEnvelope<T>).data
    if (data !== undefined) return data
  }
  return body as T
}

export type RoleRow = {
  id?: number | string
  name?: string
  slug?: string
  description?: string
  dashboard_route?: string
  priority?: number
  is_active?: number | string | boolean
  user_count?: number
}

export type PermissionRow = {
  id?: number | string
  name?: string
  description?: string
  created_at?: string
  updated_at?: string
}

export async function fetchRolesList(params?: {
  page?: number
  per_page?: number
  sort_by?: string
  sort_dir?: 'asc' | 'desc'
}): Promise<{
  roles?: RoleRow[]
  meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number }
}> {
  const qs = new URLSearchParams()
  qs.set('per_page', String(params?.per_page ?? 200))
  if (params?.page) qs.set('page', String(params.page))
  if (params?.sort_by) qs.set('sort_by', params.sort_by)
  if (params?.sort_dir) qs.set('sort_dir', params.sort_dir)
  const suffix = qs.toString() ? `?${qs}` : ''
  const body = await apiFetch<
    | {
        roles?: RoleRow[]
        meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number }
      }
    | {
        data?: {
          roles?: RoleRow[]
          meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number }
        }
      }
  >(`${BASE}/roles${suffix}`)
  return unwrapData(body)
}

export async function fetchAllRoles(): Promise<{ roles: RoleRow[] }> {
  const rolesById = new Map<string, RoleRow>()
  let page = 1
  let lastPage = 1

  do {
    const response = await fetchRolesList({
      page,
      per_page: 200,
      sort_by: 'name',
      sort_dir: 'asc',
    })
    for (const role of response.roles ?? []) {
      const key = String(role.id ?? '').trim()
      if (key === '') {
        continue
      }
      if (!rolesById.has(key)) {
        rolesById.set(key, role)
      }
    }
    lastPage = Number(response.meta?.last_page ?? page)
    page += 1
  } while (page <= lastPage)

  return { roles: [...rolesById.values()] }
}

export async function fetchPermissionsList(): Promise<{ permissions?: PermissionRow[] }> {
  const body = await apiFetch<
    { permissions?: PermissionRow[] } | { data?: { permissions?: PermissionRow[] } }
  >(`${BASE}/permissions`)
  return unwrapData(body)
}

export type AssignUserRow = {
  id?: number | string
  account_id?: string
  firstname?: string
  lastname?: string
  email?: string
  roles?: string[]
}

export async function fetchUsersForAssign(params?: {
  school_year?: string
  semester?: string
  page?: number
  per_page?: number
}): Promise<{ users?: AssignUserRow[]; meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number } }> {
  const qs = new URLSearchParams()
  if (params?.school_year) qs.set('school_year', params.school_year)
  if (params?.semester) qs.set('semester', params.semester)
  qs.set('per_page', String(params?.per_page ?? 200))
  if (params?.page) qs.set('page', String(params.page))
  qs.set('sort_by', 'lastname')
  qs.set('sort_dir', 'asc')
  const suffix = qs.toString() ? `?${qs}` : ''
  const body = await apiFetch<
    | {
        users?: AssignUserRow[]
        meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number }
      }
    | {
        data?: {
          users?: AssignUserRow[]
          meta?: { current_page?: number; last_page?: number; total?: number; per_page?: number }
        }
      }
  >(
    `${BASE}/users${suffix}`,
  )
  return unwrapData(body)
}

export async function fetchAllUsersForAssign(params?: {
  school_year?: string
  semester?: string
}): Promise<{ users: AssignUserRow[] }> {
  const usersById = new Map<string, AssignUserRow>()
  let page = 1
  let lastPage = 1

  do {
    const response = await fetchUsersForAssign({
      school_year: params?.school_year,
      semester: params?.semester,
      page,
      per_page: 200,
    })
    for (const user of response.users ?? []) {
      const key = String(user.id ?? '').trim()
      if (key === '') {
        continue
      }
      if (!usersById.has(key)) {
        usersById.set(key, user)
      }
    }
    lastPage = Number(response.meta?.last_page ?? page)
    page += 1
  } while (page <= lastPage)

  return { users: [...usersById.values()] }
}

export async function saveUserRoles(payload: {
  user_id: number | string
  role_ids: (number | string)[]
}): Promise<{ ok?: boolean; message?: string }> {
  const userId = encodeURIComponent(String(payload.user_id))
  const body = await apiFetch<{ ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }>(
    `${BASE}/users/${userId}/roles`,
    {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ role_ids: payload.role_ids }),
    },
  )
  return unwrapData(body)
}

export async function createRole(payload: Record<string, unknown>): Promise<{ ok?: boolean; message?: string }> {
  const body = await apiFetch<{ ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }>(
    `${BASE}/roles`,
    {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    },
  )
  return unwrapData(body)
}

export async function updateRole(
  roleId: number | string,
  payload: Record<string, unknown>,
): Promise<{ ok?: boolean; message?: string }> {
  try {
    const body = await apiFetch<
      { ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }
    >(`${BASE}/roles/${encodeURIComponent(String(roleId))}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    return unwrapData(body)
  } catch (error) {
    if (error instanceof ApiHttpError && error.status === 422) {
      const body = error.body
      if (body && typeof body === 'object') {
        const errors = (body as { errors?: unknown }).errors
        if (errors && typeof errors === 'object') {
          for (const value of Object.values(errors as Record<string, unknown>)) {
            if (Array.isArray(value) && value.length > 0) {
              throw new Error(String(value[0]))
            }
            if (typeof value === 'string' && value.trim() !== '') {
              throw new Error(value)
            }
          }
        }
      }
    }
    throw error
  }
}

export async function deleteRole(roleId: number | string): Promise<{ ok?: boolean }> {
  const body = await apiFetch<{ ok?: boolean } | { data?: { ok?: boolean } }>(
    `${BASE}/roles/${encodeURIComponent(String(roleId))}`,
    { method: 'DELETE' },
  )
  return unwrapData(body)
}

export async function fetchRole(roleId: number | string): Promise<{ role?: RoleRow }> {
  const body = await apiFetch<{ role?: RoleRow } | { data?: { role?: RoleRow } }>(
    `${BASE}/roles/${encodeURIComponent(String(roleId))}`,
  )
  return unwrapData(body)
}

export async function createPermission(payload: {
  name: string
  description?: string
}): Promise<{ ok?: boolean; message?: string }> {
  const body = await apiFetch<{ ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }>(
    `${BASE}/permissions`,
    {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    },
  )
  return unwrapData(body)
}

export async function fetchPermission(
  permissionId: number | string,
): Promise<{ permission?: PermissionRow }> {
  const body = await apiFetch<{ permission?: PermissionRow } | { data?: { permission?: PermissionRow } }>(
    `${BASE}/permissions/${encodeURIComponent(String(permissionId))}`,
  )
  return unwrapData(body)
}

export async function updatePermission(
  permissionId: number | string,
  payload: { name: string; description?: string },
): Promise<{ ok?: boolean; message?: string }> {
  const body = await apiFetch<{ ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }>(
    `${BASE}/permissions/${encodeURIComponent(String(permissionId))}`,
    {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    },
  )
  return unwrapData(body)
}

export type PermissionMatrixRow = {
  id?: number | string
  permission_id?: number | string
  name?: string
  description?: string
  can_create?: boolean | number
  can_read?: boolean | number
  can_update?: boolean | number
  can_delete?: boolean | number
}

export type ExistingPermissionFlags = {
  can_create?: boolean
  can_read?: boolean
  can_update?: boolean
  can_delete?: boolean
}

export async function fetchRolePermissionMatrix(roleId: number | string): Promise<{
  roleId?: number | string
  permissions?: PermissionMatrixRow[]
  existing?: Record<string, ExistingPermissionFlags>
}> {
  const body = await apiFetch<
    | {
        roleId?: number | string
        permissions?: PermissionMatrixRow[]
        existing?: Record<string, ExistingPermissionFlags>
      }
    | {
        data?: {
          roleId?: number | string
          permissions?: PermissionMatrixRow[]
          existing?: Record<string, ExistingPermissionFlags>
        }
      }
  >(`${BASE}/roles/${encodeURIComponent(String(roleId))}/permissions`)
  return unwrapData(body)
}

export async function saveRolePermissionMatrix(
  roleId: number | string,
  payload: Record<string, { create?: boolean; read?: boolean; update?: boolean; delete?: boolean }>,
): Promise<{ ok?: boolean; message?: string }> {
  const body = await apiFetch<{ ok?: boolean; message?: string } | { data?: { ok?: boolean; message?: string } }>(
    `${BASE}/roles/${encodeURIComponent(String(roleId))}/permissions`,
    {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ permissions: payload }),
    },
  )
  return unwrapData(body)
}
