import { Types } from 'mongoose';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { Team } from '@/modules/teams/teams.model';
import {
  NewCreatedTeam,
  UpdateTeamBody,
} from '@/modules/teams/teams.interface';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { validateTeamUsers } from '@/modules/teams/teams.util';
import { safeDeleteById } from '@/shared/utils/guard/ref-guard';
import { getObjectId } from '@/shared/utils/commonHelper';
import type { IUserDoc } from '@/modules/user/user.interfaces';
import {
  assertUserCanAccessTeam,
  isAdminOrSuperAdminUser,
} from '@/modules/teams/teams.access';

const { TeamResponseCodes } = responseCodes;


export const getVisibleAssigneeIdsForUser = async (
  userId: Types.ObjectId,
  teamIds: Types.ObjectId[],
): Promise<Types.ObjectId[]> => {
  const userIdStr = getObjectId(userId).toString();
  const ids = new Set<string>([userIdStr]);
  if (!teamIds?.length) return [getObjectId(userId)];

  const teams = await Team.find({ _id: { $in: teamIds } })
    .select('lead members')
    .lean();

  for (const t of teams) {
    const isTeamLead = t.lead && getObjectId(t.lead).toString() === userIdStr;
    
    if (isTeamLead) {
      // Team lead can see all members' leads
      if (t.members?.length)
        t.members.forEach((m) => ids.add(getObjectId(m).toString()));
    }
    // Team members can only see their own leads (already added to ids set)
  }

  return Array.from(ids).map((id) => getObjectId(id));
};

export const createTeam = async (data: NewCreatedTeam): Promise<boolean> => {
  await validateTeamUsers({
    company: data.companyId,
    lead: data.lead,
    members: data.members,
  });
  const team = await Team.create(data);

  if (!team)
    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to create team',
      true,
      '',
      TeamResponseCodes.TEAM_CREATION_FAILED,
    );

  return true;
};

export const getTeamById = async (id: string, requestingUser?: IUserDoc) => {
  const team = await Team.findById(id)
    .populate('members', 'firstName lastName')
    .populate('lead', 'firstName lastName');

  if (!team)
    throw new ApiError(
      defaultStatus.OK,
      'Team not found',
      true,
      '',
      TeamResponseCodes.ROLE_NOT_FOUND,
    );

  if (requestingUser) {
    assertUserCanAccessTeam(requestingUser, {
      _id: team._id as Types.ObjectId,
      companyId: team.companyId as Types.ObjectId,
    });
  }

  return team;
};

export const updateTeam = async (
  id: string,
  updateData: UpdateTeamBody,
): Promise<boolean | null> => {
  let team: boolean | null;

  try {
    const existingTeam = await Team.findOne({
      _id: id,
      companyId: updateData.companyId,
    });

    if (!existingTeam)
      throw new ApiError(
        defaultStatus.OK,
        'Team not found',
        true,
        '',
        TeamResponseCodes.TEAM_NOT_FOUND,
      );

    if (updateData.companyId || updateData.lead || updateData.members?.length)
      await validateTeamUsers({
        company: updateData.companyId ?? existingTeam.companyId,
        members: updateData.members,
      });

    team = await Team.findByIdAndUpdate(id, updateData, { new: true });
  } catch (_error) {
    console.log('🚀 ~ updateTeam ~ _error:', _error);
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update team',
      true,
      '',
      TeamResponseCodes.ROLE_ERROR,
    );
  }

  if (!team)
    throw new ApiError(
      defaultStatus.OK,
      'Team not found',
      true,
      '',
      TeamResponseCodes.ROLE_NOT_FOUND,
    );

  return true;
};

export const deleteTeam = async (id: string): Promise<boolean> => {
  try {
    await safeDeleteById(Team, id, TeamResponseCodes.TEAM_IN_USE);
    return true;
  } catch (_error) {
    throw new ApiError(
      defaultStatus.OK,
      'Failed to delete team',
      true,
      '',
      TeamResponseCodes.ROLE_ERROR,
    );
  }
};

export const queryTeams = async (
  filter: Record<string, string> = {},
  options = {},
  requestingUser?: IUserDoc,
) => {
  const { search, companyId, ...rest } = filter;

  const query: Record<string, unknown> = {
    ...rest,
    ...(companyId && { companyId: getObjectId(companyId) }),
    ...(search && {
      name: { $regex: search, $options: 'i' },
    }),
  };

  if (
    requestingUser &&
    !isAdminOrSuperAdminUser(requestingUser)
  ) {
    const teamIds = requestingUser.team ?? [];
    query._id = { $in: teamIds.length ? teamIds : [] };
  }

  return Team.paginate(query, options);
};
