import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { createHash } from "crypto";
import { TokenStorageService } from "./token-storage.service.js";

/**
 * Token Revocation Service
 *
 * Handles token revocation operations including:
 * - Single token revocation
 * - Family invalidation
 * - Bulk user token revocation
 */
@Injectable()
export class TokenRevocationService {
  constructor(
    private readonly storage: TokenStorageService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Revoke a specific refresh token
   */
  async revokeRefreshToken(refreshToken: string): Promise<void> {
    try {
      const tokenHash = this.hashToken(refreshToken);
      const tokenRecord = await this.storage.getTokenByHash(tokenHash);

      if (tokenRecord) {
        await this.storage.deleteToken(tokenHash);
        await this.storage.deleteFamily(tokenRecord.familyId);
        this.logger.debug("Revoked refresh token", { tokenHash: tokenHash.slice(0, 8) });
      }
    } catch (error) {
      this.logger.error("Failed to revoke refresh token", {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  /**
   * Invalidate an entire token family (used for security violations)
   */
  async invalidateTokenFamily(familyId: string): Promise<void> {
    try {
      const family = await this.storage.getFamily(familyId);

      if (family) {
        await this.storage.deleteFamily(familyId);
        await this.storage.deleteToken(family.tokenHash);
        await this.storage.removeFromUserSet(family.userId, familyId);

        this.logger.warn("Invalidated token family due to security concern", {
          familyId: familyId.slice(0, 8),
          userId: family.userId,
        });
      }
    } catch (error) {
      this.logger.error("Failed to invalidate token family", {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  /**
   * Revoke all refresh tokens for a user
   */
  async revokeAllUserTokens(userId: string): Promise<void> {
    try {
      const familyIds = await this.storage.getUserFamilyIds(userId);

      if (familyIds.length === 0) {
        this.logger.debug("No tokens found for user", { userId });
        return;
      }

      // Get all family data to find token hashes
      for (const familyId of familyIds) {
        const family = await this.storage.getFamily(familyId);
        await this.storage.deleteFamily(familyId);
        if (family?.tokenHash) {
          await this.storage.deleteToken(family.tokenHash);
        }
      }

      // Delete the user's token set
      await this.storage.deleteUserSet(userId);

      this.logger.debug("Revoked all tokens for user", {
        userId,
        tokenCount: familyIds.length,
      });
    } catch (error) {
      this.logger.error("Failed to revoke all user tokens", {
        error: error instanceof Error ? error.message : String(error),
        userId,
      });

      // Fallback to scan method
      await this.revokeAllUserTokensFallback(userId);
    }
  }

  /**
   * Fallback method for revoking all user tokens using scan
   */
  private async revokeAllUserTokensFallback(userId: string): Promise<void> {
    try {
      let cursor = "0";
      const pattern = `${this.storage.REFRESH_TOKEN_FAMILY_PREFIX}*`;

      do {
        const [nextCursor, keys] = await this.storage.redis.scan(
          cursor,
          "MATCH",
          pattern,
          "COUNT",
          100
        );
        cursor = nextCursor;

        if (keys && keys.length) {
          for (const key of keys) {
            const data = await this.storage.redis.get(key);
            if (!data) continue;

            const family = this.storage.parseRefreshTokenFamilyRecord(data);
            if (family && family.userId === userId) {
              await this.storage.redis.del(key);
              await this.storage.deleteToken(family.tokenHash);
            }
          }
        }
      } while (cursor !== "0");

      this.logger.debug("Revoked all tokens for user (fallback method)", { userId });
    } catch (error) {
      this.logger.error("Failed to revoke all user tokens (fallback)", {
        error: error instanceof Error ? error.message : String(error),
        userId,
      });
    }
  }

  /**
   * Get all active refresh token families for a user
   */
  async getUserRefreshTokenFamilies(userId: string): Promise<
    Array<{
      familyId: string;
      deviceId?: string | undefined;
      userAgent?: string | undefined;
      createdAt?: string | undefined;
    }>
  > {
    try {
      const familyIds = await this.storage.getUserFamilyIds(userId);

      const families: Array<{
        familyId: string;
        deviceId?: string | undefined;
        userAgent?: string | undefined;
        createdAt?: string | undefined;
      }> = [];

      for (const familyId of familyIds) {
        const family = await this.storage.getFamily(familyId);
        if (family) {
          families.push({
            familyId,
            deviceId: family.deviceId,
            userAgent: family.userAgent,
            createdAt: family.createdAt,
          });
        }
      }

      return families.sort((a, b) => {
        const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
        const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
        return timeB - timeA;
      });
    } catch (error) {
      this.logger.error("Failed to get user refresh token families", {
        error: error instanceof Error ? error.message : String(error),
        userId,
      });
      return [];
    }
  }

  private hashToken(token: string): string {
    return createHash("sha256").update(token).digest("hex");
  }
}
