import { Injectable, Logger } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { PrismaService } from "../database/prisma.service";
import {
  FINISHED_STATE_VALUES_FOR_DB,
  FINISHED_STATUS_VALUES_FOR_DB,
  LIVE_STATE_VALUES_FOR_DB,
  LIVE_STATUS_VALUES_FOR_DB,
} from "../common/utils/match-status.util";
import {
  getDateOnlyValueForTimeZone,
  getShiftedDateStringInTimeZone,
  getDayBoundsForTimeZone,
} from "../common/utils/timezone.util";
import { TaskLockService } from "./task-lock.service";

@Injectable()
export class LimitResetterTask {
  private readonly logger = new Logger(LimitResetterTask.name);
  private readonly timeZone = "Europe/Istanbul";

  constructor(
    private readonly prisma: PrismaService,
    private readonly taskLock: TaskLockService,
  ) {}

  private shouldSkipInHistoricalMode(jobName: string): boolean {
    if (process.env.FEEDER_MODE === "historical") {
      this.logger.debug(`Skipping ${jobName} in historical feeder mode`);
      return true;
    }
    return false;
  }

  /**
   * Reset usage limits daily at 03:00 (Europe/Istanbul)
   */
  @Cron("0 3 * * *", { timeZone: "Europe/Istanbul" })
  async resetUsageLimits() {
    if (this.shouldSkipInHistoricalMode("resetUsageLimits")) return;
    await this.taskLock.runWithLease(
      "resetUsageLimits",
      30 * 60 * 1000,
      async () => {
        this.logger.log("Starting daily usage limit reset job...");

        try {
          const today = getDateOnlyValueForTimeZone(this.timeZone);

          const result = await this.prisma.usageLimit.updateMany({
            where: {
              lastResetDate: { lt: today },
            },
            data: {
              analysisCount: 0,
              couponCount: 0,
              lastResetDate: today,
            },
          });

          if (result.count > 0) {
            this.logger.log(
              `Usage limits for ${result.count} users have been reset`,
            );
          } else {
            this.logger.log("No user limits needed resetting");
          }
        } catch (error: any) {
          this.logger.error(`Limit reset job failed: ${error.message}`);
        }
      },
      this.logger,
    );
  }

  /**
   * Clean up old predictions (older than 30 days)
   */
  @Cron("0 4 * * *", { timeZone: "Europe/Istanbul" })
  async cleanupOldData() {
    if (this.shouldSkipInHistoricalMode("cleanupOldData")) return;
    await this.taskLock.runWithLease(
      "cleanupOldData",
      60 * 60 * 1000,
      async () => {
        this.logger.log("Starting data cleanup job...");

        try {
          const thirtyDaysAgo = new Date();
          thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

          const deletedLogs = await this.prisma.aiPredictionsLog.deleteMany({
            where: {
              createdAt: { lt: thirtyDaysAgo },
            },
          });

          const yesterdayDate = getShiftedDateStringInTimeZone(
            -1,
            this.timeZone,
          );
          const { startMs: yesterdayStartMs } = getDayBoundsForTimeZone(
            yesterdayDate,
            this.timeZone,
          );
          const liveMatchCutoff = new Date(yesterdayStartMs);

          const deletedLiveMatches = await this.prisma.liveMatch.deleteMany({
            where: {
              updatedAt: { lt: liveMatchCutoff },
              OR: [
                { status: { in: FINISHED_STATUS_VALUES_FOR_DB } },
                { state: { in: FINISHED_STATE_VALUES_FOR_DB } },
                {
                  AND: [
                    { scoreHome: { not: null } },
                    { scoreAway: { not: null } },
                    {
                      NOT: {
                        OR: [
                          { status: { in: LIVE_STATUS_VALUES_FOR_DB } },
                          { state: { in: LIVE_STATE_VALUES_FOR_DB } },
                        ],
                      },
                    },
                  ],
                },
              ],
            },
          });

          this.logger.log(
            `Cleanup complete: ${deletedLogs.count} old logs, ${deletedLiveMatches.count} old live matches`,
          );
        } catch (error: any) {
          this.logger.error(`Cleanup job failed: ${error.message}`);
        }
      },
      this.logger,
    );
  }

  /**
   * Downgrade cancelled subscriptions that have passed their cancel effective date
   */
  @Cron("0 0 * * *", { timeZone: "Europe/Istanbul" })
  async checkSubscriptions() {
    if (this.shouldSkipInHistoricalMode("checkSubscriptions")) return;
    await this.taskLock.runWithLease(
      "checkSubscriptions",
      30 * 60 * 1000,
      async () => {
        this.logger.log("Checking expired subscriptions...");

        try {
          const now = new Date();

          // Find subscriptions with passed cancel effective date
          const expiredSubs = await this.prisma.subscription.findMany({
            where: {
              plan: "cancelled",
              cancelEffectiveDate: { lt: now },
            },
            select: { id: true, userId: true },
          });

          for (const sub of expiredSubs) {
            // Downgrade to free
            await this.prisma.user.update({
              where: { id: sub.userId },
              data: { subscriptionStatus: "free" },
            });

            // Sync limits to free tier
            await this.prisma.usageLimit.upsert({
              where: { userId: sub.userId },
              update: { maxAnalyses: 3, maxCoupons: 1 },
              create: {
                userId: sub.userId,
                analysisCount: 0,
                couponCount: 0,
                maxAnalyses: 3,
                maxCoupons: 1,
                lastResetDate: new Date(),
              },
            });

            // Reset subscription to free
            await this.prisma.subscription.update({
              where: { id: sub.id },
              data: {
                plan: "free",
                cancelledAt: null,
                cancelEffectiveDate: null,
              },
            });
          }

          if (expiredSubs.length > 0) {
            this.logger.log(
              `${expiredSubs.length} cancelled subscriptions downgraded to free`,
            );
          }
        } catch (error: unknown) {
          const err = error as Error;
          this.logger.error(`Subscription check failed: ${err.message}`);
        }
      },
      this.logger,
    );
  }
}
