import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  UseInterceptors,
  Inject,
  NotFoundException,
  BadRequestException,
} from "@nestjs/common";
import {
  CacheInterceptor,
  CacheKey,
  CacheTTL,
  CACHE_MANAGER,
} from "@nestjs/cache-manager";
import * as cacheManager from "cache-manager";
import {
  ApiTags,
  ApiBearerAuth,
  ApiOperation,
  ApiResponse as SwaggerResponse,
} from "@nestjs/swagger";
import { Roles } from "../../common/decorators";
import { PrismaService } from "../../database/prisma.service";
import { PaginationDto } from "../../common/dto/pagination.dto";
import { AdminUsersQueryDto } from "./dto/admin-users-query.dto";
import { UpdateUserSubscriptionDto } from "./dto/update-user-subscription.dto";
import {
  ApiResponse,
  createSuccessResponse,
  createPaginatedResponse,
  PaginatedData,
} from "../../common/types/api-response.type";
import { plainToInstance } from "class-transformer";
import { UserResponseDto } from "../users/dto/user.dto";
import { UserRole } from "@prisma/client";
import { SubscriptionsService } from "../subscriptions/subscriptions.service";
import { PlanType } from "../subscriptions/dto/subscription.dto";

@ApiTags("Admin")
@ApiBearerAuth()
@Controller("admin")
@Roles("superadmin")
export class AdminController {
  constructor(
    private readonly prisma: PrismaService,
    @Inject(CACHE_MANAGER) private cacheManager: cacheManager.Cache,
    private readonly subscriptionsService: SubscriptionsService,
  ) {}

  // ================== Users Management ==================

  @Get("users")
  @ApiOperation({ summary: "Get all users (admin)" })
  @SwaggerResponse({ status: 200, type: [UserResponseDto] })
  async getAllUsers(
    @Query() query: AdminUsersQueryDto,
  ): Promise<ApiResponse<PaginatedData<UserResponseDto>>> {
    const { skip, take, orderBy, search, role, subscriptionStatus } = query;

    const where: any = {};
    if (search) {
      where.OR = [
        { email: { contains: search, mode: "insensitive" } },
        { firstName: { contains: search, mode: "insensitive" } },
        { lastName: { contains: search, mode: "insensitive" } },
      ];
    }
    if (role) {
      where.role = role;
    }
    if (subscriptionStatus) {
      where.subscriptionStatus = subscriptionStatus;
    }

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where,
        skip,
        take,
        orderBy,
      }),
      this.prisma.user.count({ where }),
    ]);

    const dtos = plainToInstance(
      UserResponseDto,
      users,
    ) as unknown as UserResponseDto[];

    return createPaginatedResponse(
      dtos,
      total,
      query.page || 1,
      query.limit || 10,
    );
  }

  @Get("users/:id")
  @ApiOperation({ summary: "Get user by ID" })
  @SwaggerResponse({ status: 200, type: UserResponseDto })
  async getUserById(
    @Param("id") id: string,
  ): Promise<ApiResponse<UserResponseDto>> {
    const user = await this.prisma.user.findUnique({
      where: { id },
      include: {
        usageLimit: true,
        analyses: {
          take: 5,
          orderBy: { createdAt: "desc" },
        },
      },
    });

    if (!user) {
      throw new NotFoundException("User not found");
    }

    return createSuccessResponse(plainToInstance(UserResponseDto, user));
  }

  @Put("users/:id/toggle-active")
  @ApiOperation({ summary: "Toggle user active status" })
  @SwaggerResponse({ status: 200, type: UserResponseDto })
  async toggleUserActive(
    @Param("id") id: string,
  ): Promise<ApiResponse<UserResponseDto>> {
    const user = await this.prisma.user.findUnique({ where: { id } });

    if (!user) {
      throw new NotFoundException("User not found");
    }

    const updated = await this.prisma.user.update({
      where: { id },
      data: { isActive: !user.isActive },
    });

    return createSuccessResponse(
      plainToInstance(UserResponseDto, updated),
      "common.SUCCESS_USER_STATUS_UPDATED",
    );
  }

  @Put("users/:id/role")
  @ApiOperation({ summary: "Update user role" })
  @SwaggerResponse({ status: 200, type: UserResponseDto })
  async updateUserRole(
    @Param("id") id: string,
    @Body() data: { role: UserRole },
  ): Promise<ApiResponse<UserResponseDto>> {
    const user = await this.prisma.user.update({
      where: { id },
      data: { role: data.role },
    });

    return createSuccessResponse(
      plainToInstance(UserResponseDto, user),
      "common.SUCCESS_USER_ROLE_UPDATED",
    );
  }

  @Delete("users/:id")
  @ApiOperation({ summary: "Soft delete a user" })
  @SwaggerResponse({ status: 200, description: "User deleted" })
  async deleteUser(@Param("id") id: string): Promise<ApiResponse<null>> {
    await this.prisma.user.update({
      where: { id },
      data: { deletedAt: new Date() },
    });
    return createSuccessResponse(null, "common.SUCCESS_USER_DELETED");
  }

  // ================== App Settings ==================

  @Get("settings")
  @UseInterceptors(CacheInterceptor)
  @CacheKey("app_settings")
  @CacheTTL(60 * 1000)
  @ApiOperation({ summary: "Get all app settings" })
  @SwaggerResponse({
    status: 200,
    schema: { type: "object", additionalProperties: { type: "string" } },
  })
  async getAllSettings(): Promise<ApiResponse<Record<string, string>>> {
    const settings = await this.prisma.appSetting.findMany();
    const settingsMap: Record<string, string> = {};
    for (const s of settings) {
      settingsMap[s.key] = s.value || "";
    }
    return createSuccessResponse(settingsMap);
  }

  @Put("settings/:key")
  @ApiOperation({ summary: "Update an app setting" })
  @SwaggerResponse({
    status: 200,
    schema: {
      type: "object",
      properties: { key: { type: "string" }, value: { type: "string" } },
    },
  })
  async updateSetting(
    @Param("key") key: string,
    @Body() data: { value: string },
  ): Promise<ApiResponse<{ key: string; value: string }>> {
    const setting = await this.prisma.appSetting.upsert({
      where: { key },
      update: { value: data.value },
      create: { key, value: data.value },
    });
    await this.cacheManager.del("app_settings");
    return createSuccessResponse(
      { key: setting.key, value: setting.value || "" },
      "common.SUCCESS_SETTING_UPDATED",
    );
  }

  // ================== Usage Limits ==================

  @Get("usage-limits")
  @ApiOperation({ summary: "Get all usage limits" })
  @SwaggerResponse({
    status: 200,
    schema: { type: "array", items: { type: "object" } },
  })
  async getAllUsageLimits(@Query() pagination: PaginationDto) {
    const { skip, take } = pagination;

    const [limits, total] = await Promise.all([
      this.prisma.usageLimit.findMany({
        skip,
        take,
        include: {
          user: {
            select: { id: true, email: true, firstName: true, lastName: true },
          },
        },
        orderBy: { lastResetDate: "desc" },
      }),
      this.prisma.usageLimit.count(),
    ]);

    return createPaginatedResponse(
      limits,
      total,
      pagination.page || 1,
      pagination.limit || 10,
    );
  }

  @Post("usage-limits/reset-all")
  @ApiOperation({ summary: "Reset all usage limits" })
  @SwaggerResponse({
    status: 200,
    schema: { type: "object", properties: { count: { type: "number" } } },
  })
  async resetAllUsageLimits(): Promise<ApiResponse<{ count: number }>> {
    const result = await this.prisma.usageLimit.updateMany({
      data: {
        analysisCount: 0,
        couponCount: 0,
        lastResetDate: new Date(),
      },
    });

    return createSuccessResponse(
      { count: result.count },
      "common.SUCCESS_ALL_LIMITS_RESET",
    );
  }

  @Post("usage-limits/reset/:userId")
  @ApiOperation({ summary: "Reset usage limits for a single user" })
  @SwaggerResponse({ status: 200 })
  async resetUserUsageLimits(
    @Param("userId") userId: string,
  ): Promise<ApiResponse<null>> {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) throw new NotFoundException("USER_NOT_FOUND");

    await this.prisma.usageLimit.update({
      where: { userId },
      data: {
        analysisCount: 0,
        couponCount: 0,
        lastResetDate: new Date(),
      },
    });

    return createSuccessResponse(null, "common.SUCCESS_USER_LIMITS_RESET");
  }

  @Put("users/:userId/subscription")
  @ApiOperation({ summary: "Update a user's subscription tier" })
  @SwaggerResponse({ status: 200 })
  async updateUserSubscription(
    @Param("userId") userId: string,
    @Body() data: UpdateUserSubscriptionDto,
  ): Promise<ApiResponse<null>> {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) throw new NotFoundException("USER_NOT_FOUND");

    const validPlans = [
      PlanType.FREE,
      PlanType.PLUS,
      PlanType.PREMIUM,
      "past_due",
      "cancelled",
    ];
    const newPlan = data.plan as PlanType;
    if (!validPlans.includes(newPlan)) {
      throw new BadRequestException("INVALID_PLAN_TYPE");
    }

    const updateData: any = { subscriptionStatus: newPlan };

    if (data.expiresAt) {
      const parsedDate = new Date(data.expiresAt);

      // Business Logic: If upgrading to Premium/Plus, the expiry date cannot be in the past
      const today = new Date();
      today.setHours(0, 0, 0, 0); // Strip time

      const expiry = new Date(parsedDate);
      expiry.setHours(0, 0, 0, 0);

      if (
        (newPlan === PlanType.PREMIUM || newPlan === PlanType.PLUS) &&
        expiry < today
      ) {
        throw new BadRequestException("EXPIRES_AT_CANNOT_BE_IN_PAST");
      }

      updateData.subscriptionExpiresAt = parsedDate;
    } else if (data.expiresAt === null) {
      updateData.subscriptionExpiresAt = null;
    }

    await this.prisma.user.update({
      where: { id: userId },
      data: updateData,
    });

    await this.subscriptionsService.syncLimitsWithPlan(userId, newPlan);

    return createSuccessResponse(
      null,
      "common.SUCCESS_USER_SUBSCRIPTION_UPDATED",
    );
  }

  // ================== Analytics ==================

  @Get("analytics/overview")
  @ApiOperation({ summary: "Get system analytics overview" })
  @SwaggerResponse({ status: 200, schema: { type: "object" } })
  async getAnalyticsOverview() {
    const [
      totalUsers,
      activeUsers,
      premiumUsers,
      totalMatches,
      totalPredictions,
      totalCoupons,
    ] = await Promise.all([
      this.prisma.user.count(),
      this.prisma.user.count({ where: { isActive: true } }),
      this.prisma.user.count({
        where: { subscriptionStatus: { in: ["plus", "premium"] } },
      }),
      this.prisma.match.count(),
      this.prisma.prediction.count(),
      this.prisma.userCoupon.count(),
    ]);

    return createSuccessResponse({
      totalUsers,
      activeUsers,
      totalPredictions,
      totalCoupons,
      users: {
        total: totalUsers,
        active: activeUsers,
        premium: premiumUsers,
      },
      matches: totalMatches,
      predictions: totalPredictions,
    });
  }
}
