import { hash } from 'bcryptjs';
import { asc, eq } from 'drizzle-orm';
import { NextRequest, NextResponse } from 'next/server';
import { memberships, organizations, users } from '@/db/schema';
import { db } from '@/lib/db';
import { AuthError } from '@/lib/auth/session';
import {
  buildMembershipValues,
  buildUserResponseModels,
  parseUserMutationPayload,
  requireUserManagementAccess,
  syncCrmRoleAssignmentsForUser,
  validateManagedOrganizations
} from './_lib';

function applyRoleFilter(usersList: Awaited<ReturnType<typeof buildUserResponseModels>>, roles?: string) {
  if (!roles) {
    return usersList;
  }

  const selectedRoles = roles
    .split(/[.,]/)
    .map((value) => value.trim())
    .filter(Boolean);

  return usersList.filter(
    (user) =>
      selectedRoles.includes(user.systemRole) ||
      user.memberships.some((membership) => selectedRoles.includes(membership.membershipRole))
  );
}

function sortUsers(usersList: Awaited<ReturnType<typeof buildUserResponseModels>>, sort?: string) {
  const defaultSort = usersList.toSorted((a, b) => a.name.localeCompare(b.name));

  if (!sort) {
    return defaultSort;
  }

  try {
    const sortItems = JSON.parse(sort) as Array<{ id: string; desc: boolean }>;
    const [primarySort] = sortItems;

    if (!primarySort) {
      return defaultSort;
    }

    const sorted = usersList.toSorted((a, b) => {
      const left =
        primarySort.id === 'role'
          ? `${a.systemRole}:${a.activeMembershipRole ?? ''}`
          : primarySort.id === 'primaryCrmRole'
            ? a.primaryCrmRole ?? ''
            : primarySort.id === 'crmRoleCount'
              ? a.crmRoleCount
              : primarySort.id === 'branchScopeSummary'
                ? a.branchScopeSummary
                : primarySort.id === 'productScopeSummary'
                  ? a.productScopeSummary
          : primarySort.id === 'organizations'
            ? a.memberships.map((membership) => membership.organizationName).join(', ')
            : (a[primarySort.id as 'name' | 'email'] ?? a.name);
      const right =
        primarySort.id === 'role'
          ? `${b.systemRole}:${b.activeMembershipRole ?? ''}`
          : primarySort.id === 'primaryCrmRole'
            ? b.primaryCrmRole ?? ''
            : primarySort.id === 'crmRoleCount'
              ? b.crmRoleCount
              : primarySort.id === 'branchScopeSummary'
                ? b.branchScopeSummary
                : primarySort.id === 'productScopeSummary'
                  ? b.productScopeSummary
          : primarySort.id === 'organizations'
            ? b.memberships.map((membership) => membership.organizationName).join(', ')
            : (b[primarySort.id as 'name' | 'email'] ?? b.name);

      return String(left).localeCompare(String(right));
    });

    return primarySort.desc ? sorted.toReversed() : sorted;
  } catch {
    return defaultSort;
  }
}

export async function GET(request: NextRequest) {
  try {
    const { isSuperAdmin, manageableOrganizationIds } = await requireUserManagementAccess();
    const { searchParams } = request.nextUrl;
    const page = Number(searchParams.get('page') ?? 1);
    const limit = Number(searchParams.get('limit') ?? 10);
    const roles = searchParams.get('roles') ?? undefined;
    const search = searchParams.get('search') ?? undefined;
    const sort = searchParams.get('sort') ?? undefined;

    const usersList = await buildUserResponseModels({
      ...(isSuperAdmin ? {} : { scopedOrganizationIds: manageableOrganizationIds })
    });
    const searchTerm = search?.trim().toLowerCase();
    const searchedUsers = searchTerm
      ? usersList.filter(
          (row) =>
            row.name.toLowerCase().includes(searchTerm) ||
            row.email.toLowerCase().includes(searchTerm)
        )
      : usersList;
    const filteredUsers = applyRoleFilter(searchedUsers, roles);
    const sortedUsers = sortUsers(filteredUsers, sort);
    const offset = (page - 1) * limit;
    const pagedUsers = sortedUsers.slice(offset, offset + limit);

    return NextResponse.json({
      success: true,
      time: new Date().toISOString(),
      message: 'Users loaded successfully',
      total_users: sortedUsers.length,
      offset,
      limit,
      users: pagedUsers
    });
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

    return NextResponse.json({ message: 'Unable to load users' }, { status: 500 });
  }
}

export async function POST(request: NextRequest) {
  try {
    const { session, isSuperAdmin, manageableOrganizationIds } = await requireUserManagementAccess();
    const payload = parseUserMutationPayload(await request.json());

    if (!payload.password || payload.password.length < 8) {
      return NextResponse.json(
        { message: 'Password must be at least 8 characters' },
        { status: 400 }
      );
    }

    if (payload.systemRole === 'super_admin') {
      return NextResponse.json(
        { message: 'Creating new super admin users is not supported in this form' },
        { status: 400 }
      );
    }

    await validateManagedOrganizations(
      payload.memberships,
      payload.crmRoleAssignments,
      manageableOrganizationIds,
      isSuperAdmin
    );

    const existingUser = await db.query.users.findFirst({
      where: eq(users.email, payload.email)
    });

    if (existingUser) {
      return NextResponse.json(
        { message: 'An account with this email already exists' },
        { status: 409 }
      );
    }

    const userId = crypto.randomUUID();
    const passwordHash = await hash(payload.password, 10);

    await db.insert(users).values({
      id: userId,
      name: payload.name,
      email: payload.email,
      passwordHash,
      systemRole: payload.systemRole,
      activeOrganizationId: payload.memberships[0]?.organizationId ?? null
    });

    const membershipValues = await buildMembershipValues(
      userId,
      payload.memberships,
      payload.crmRoleAssignments
    );

    await db.insert(memberships).values(membershipValues);
    await syncCrmRoleAssignmentsForUser(
      userId,
      session.user.id,
      payload.memberships,
      payload.crmRoleAssignments
    );

    return NextResponse.json(
      {
        success: true,
        message: 'User created successfully'
      },
      { status: 201 }
    );
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

    if (error instanceof Error) {
      return NextResponse.json({ message: error.message }, { status: 400 });
    }

    return NextResponse.json({ message: 'Unable to create user' }, { status: 500 });
  }
}
