import {
  BadRequestException,
  ConflictException,
  Injectable,
  Logger,
  NotFoundException,
} from "@nestjs/common";
import { Prisma, WorkspaceMemberRole } from "@prisma/client";
import { randomUUID } from "node:crypto";
import { PrismaService } from "../prisma/prisma.service";
import type { InviteUserDto } from "./dto/invite-user.dto";
import type { UpdateUserDto } from "./dto/update-user.dto";
import type { CreateWorkspaceDto } from "./dto/create-workspace.dto";
import type {
  AdminUserResponse,
  AdminWorkspaceResponse,
  InvitationResponse,
  PaginatedResponse,
} from "./types/admin.types";

@Injectable()
export class AdminService {
  private readonly logger = new Logger(AdminService.name);

  constructor(private readonly prisma: PrismaService) {}

  async listUsers(page = 1, limit = 50): Promise<PaginatedResponse<AdminUserResponse>> {
    const skip = (page - 1) * limit;

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        include: {
          workspaceMemberships: {
            include: {
              workspace: { select: { id: true, name: true } },
            },
          },
        },
        orderBy: { createdAt: "desc" },
        skip,
        take: limit,
      }),
      this.prisma.user.count(),
    ]);

    return {
      data: users.map((user) => ({
        id: user.id,
        name: user.name,
        email: user.email,
        emailVerified: user.emailVerified,
        image: user.image,
        createdAt: user.createdAt,
        deactivatedAt: user.deactivatedAt,
        isLocalAuth: user.isLocalAuth,
        invitedAt: user.invitedAt,
        invitedBy: user.invitedBy,
        workspaceMemberships: user.workspaceMemberships.map((m) => ({
          workspaceId: m.workspaceId,
          workspaceName: m.workspace.name,
          role: m.role,
          joinedAt: m.joinedAt,
        })),
      })),
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  async inviteUser(dto: InviteUserDto, inviterId: string): Promise<InvitationResponse> {
    const existing = await this.prisma.user.findUnique({
      where: { email: dto.email },
    });

    if (existing) {
      throw new ConflictException(`User with email ${dto.email} already exists`);
    }

    if (dto.workspaceId) {
      const workspace = await this.prisma.workspace.findUnique({
        where: { id: dto.workspaceId },
      });
      if (!workspace) {
        throw new NotFoundException(`Workspace ${dto.workspaceId} not found`);
      }
    }

    const invitationToken = randomUUID();
    const now = new Date();

    const user = await this.prisma.$transaction(async (tx) => {
      const created = await tx.user.create({
        data: {
          email: dto.email,
          name: dto.name ?? dto.email.split("@")[0] ?? dto.email,
          emailVerified: false,
          invitedBy: inviterId,
          invitationToken,
          invitedAt: now,
        },
      });

      if (dto.workspaceId) {
        await tx.workspaceMember.create({
          data: {
            workspaceId: dto.workspaceId,
            userId: created.id,
            role: dto.role ?? WorkspaceMemberRole.MEMBER,
          },
        });
      }

      return created;
    });

    this.logger.log(`User invited: ${user.email} by ${inviterId}`);

    return {
      userId: user.id,
      invitationToken,
      email: user.email,
      invitedAt: now,
    };
  }

  async updateUser(id: string, dto: UpdateUserDto): Promise<AdminUserResponse> {
    const existing = await this.prisma.user.findUnique({ where: { id } });
    if (!existing) {
      throw new NotFoundException(`User ${id} not found`);
    }

    const data: Prisma.UserUpdateInput = {};

    if (dto.name !== undefined) {
      data.name = dto.name;
    }
    if (dto.emailVerified !== undefined) {
      data.emailVerified = dto.emailVerified;
    }
    if (dto.preferences !== undefined) {
      data.preferences = dto.preferences as Prisma.InputJsonValue;
    }
    if (dto.deactivatedAt !== undefined) {
      data.deactivatedAt = dto.deactivatedAt ? new Date(dto.deactivatedAt) : null;
    }

    const user = await this.prisma.user.update({
      where: { id },
      data,
      include: {
        workspaceMemberships: {
          include: {
            workspace: { select: { id: true, name: true } },
          },
        },
      },
    });

    this.logger.log(`User updated: ${id}`);

    return {
      id: user.id,
      name: user.name,
      email: user.email,
      emailVerified: user.emailVerified,
      image: user.image,
      createdAt: user.createdAt,
      deactivatedAt: user.deactivatedAt,
      isLocalAuth: user.isLocalAuth,
      invitedAt: user.invitedAt,
      invitedBy: user.invitedBy,
      workspaceMemberships: user.workspaceMemberships.map((m) => ({
        workspaceId: m.workspaceId,
        workspaceName: m.workspace.name,
        role: m.role,
        joinedAt: m.joinedAt,
      })),
    };
  }

  async deactivateUser(id: string): Promise<AdminUserResponse> {
    const existing = await this.prisma.user.findUnique({ where: { id } });
    if (!existing) {
      throw new NotFoundException(`User ${id} not found`);
    }

    if (existing.deactivatedAt) {
      throw new BadRequestException(`User ${id} is already deactivated`);
    }

    const [user] = await this.prisma.$transaction([
      this.prisma.user.update({
        where: { id },
        data: { deactivatedAt: new Date() },
        include: {
          workspaceMemberships: {
            include: {
              workspace: { select: { id: true, name: true } },
            },
          },
        },
      }),
      this.prisma.session.deleteMany({ where: { userId: id } }),
    ]);

    this.logger.log(`User deactivated and sessions invalidated: ${id}`);

    return {
      id: user.id,
      name: user.name,
      email: user.email,
      emailVerified: user.emailVerified,
      image: user.image,
      createdAt: user.createdAt,
      deactivatedAt: user.deactivatedAt,
      isLocalAuth: user.isLocalAuth,
      invitedAt: user.invitedAt,
      invitedBy: user.invitedBy,
      workspaceMemberships: user.workspaceMemberships.map((m) => ({
        workspaceId: m.workspaceId,
        workspaceName: m.workspace.name,
        role: m.role,
        joinedAt: m.joinedAt,
      })),
    };
  }

  async createWorkspace(dto: CreateWorkspaceDto): Promise<AdminWorkspaceResponse> {
    const owner = await this.prisma.user.findUnique({ where: { id: dto.ownerId } });
    if (!owner) {
      throw new NotFoundException(`User ${dto.ownerId} not found`);
    }

    const workspace = await this.prisma.$transaction(async (tx) => {
      const created = await tx.workspace.create({
        data: {
          name: dto.name,
          ownerId: dto.ownerId,
          settings: dto.settings ? (dto.settings as Prisma.InputJsonValue) : {},
        },
      });

      await tx.workspaceMember.create({
        data: {
          workspaceId: created.id,
          userId: dto.ownerId,
          role: WorkspaceMemberRole.OWNER,
        },
      });

      return created;
    });

    this.logger.log(`Workspace created: ${workspace.id} with owner ${dto.ownerId}`);

    return {
      id: workspace.id,
      name: workspace.name,
      ownerId: workspace.ownerId,
      settings: workspace.settings as Record<string, unknown>,
      createdAt: workspace.createdAt,
      updatedAt: workspace.updatedAt,
      memberCount: 1,
    };
  }

  async updateWorkspace(
    id: string,
    dto: { name?: string; settings?: Record<string, unknown> }
  ): Promise<AdminWorkspaceResponse> {
    const existing = await this.prisma.workspace.findUnique({ where: { id } });
    if (!existing) {
      throw new NotFoundException(`Workspace ${id} not found`);
    }

    const data: Prisma.WorkspaceUpdateInput = {};

    if (dto.name !== undefined) {
      data.name = dto.name;
    }
    if (dto.settings !== undefined) {
      data.settings = dto.settings as Prisma.InputJsonValue;
    }

    const workspace = await this.prisma.workspace.update({
      where: { id },
      data,
      include: {
        _count: { select: { members: true } },
      },
    });

    this.logger.log(`Workspace updated: ${id}`);

    return {
      id: workspace.id,
      name: workspace.name,
      ownerId: workspace.ownerId,
      settings: workspace.settings as Record<string, unknown>,
      createdAt: workspace.createdAt,
      updatedAt: workspace.updatedAt,
      memberCount: workspace._count.members,
    };
  }
}
