import { BaseRule } from './BaseRule.js';
import { buildRemediationFromChallenges } from '../remediation.js';

export class ChallengeRule extends BaseRule {
  constructor(arbiter) {
    super(arbiter);
  }

  evaluate(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options = {}) {
    const { includeMeta = true } = options;
    const subjectKey = this._resolveSubjectKey(rule, userKey, objectKey, options);
    const partialContext = options.partialGraphContext || null;
    const challenge = rule.challenge || rule.name || rule.relation || currentRelation;
    const withinMs = this._resolveWithinMs(rule);

    if (!partialContext || !subjectKey || !challenge) {
      const required = [this._buildRequirement(challenge, subjectKey, withinMs, 'missing_context')];
      const remediation = buildRemediationFromChallenges(required);
      return this._createStandardResult({
        possibility: 0,
        remediation,
        meta_allow: {
          ruleType: 'challenge',
          remediation
        },
        reason: 'challenge_missing_context'
      });
    }

    const subjectId = this.arbiter.resolveNodeId(subjectKey, { partialGraphContext: partialContext });
    const now = options.now !== undefined && options.now !== null ? options.now : Date.now();
    const proof = partialContext.getChallengeProof(challenge, subjectId, withinMs, now);

    if (!proof) {
      const required = [this._buildRequirement(challenge, subjectKey, withinMs, 'missing')];
      const remediation = buildRemediationFromChallenges(required);
      return this._createStandardResult({
        possibility: 0,
        remediation,
        meta_allow: {
          ruleType: 'challenge',
          remediation
        },
        reason: 'challenge_missing'
      });
    }

    return this._createStandardResult({
      possibility: 1,
      ...(includeMeta && {
        meta: {
          ruleType: 'challenge',
          reason: 'challenge_satisfied',
          challenge,
          subject: subjectKey,
          issuedAt: proof.issuedAt,
          expiresAt: proof.expiresAt || null
        }
      }),
      reason: 'challenge_satisfied'
    });
  }

  _resolveSubjectKey(rule, userKey, objectKey, options) {
    if (rule.subjectKey) return rule.subjectKey;
    const subject = rule.subject || 'user';
    if (subject === 'object') return objectKey;
    if (subject === 'session') return options.sessionKey || userKey;
    return userKey;
  }

  _resolveWithinMs(rule) {
    if (rule.withinMs !== undefined && rule.withinMs !== null) return rule.withinMs;
    if (rule.withinSeconds !== undefined && rule.withinSeconds !== null) return rule.withinSeconds * 1000;
    if (rule.withinMinutes !== undefined && rule.withinMinutes !== null) return rule.withinMinutes * 60 * 1000;
    if (rule.withinHours !== undefined && rule.withinHours !== null) return rule.withinHours * 60 * 60 * 1000;
    return null;
  }

  _buildRequirement(challenge, subjectKey, withinMs, status) {
    return {
      name: challenge,
      subject: subjectKey,
      withinMs: withinMs || null,
      status
    };
  }
}
