/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 * Feature Store - Storage and retrieval of user feature vectors
 * [1.2] Feature Store and Cosmos containers
 */

import { getRegisteredContainer } from '@bytelyst/cosmos';
import type { CompleteFeatureVector } from './feature-extractor.js';
import type { UserFeatureVectorDoc, FeatureDefinition } from './types.js';

const FEATURE_SCHEMA_VERSION = '1.0.0';
const DEFAULT_TTL_SECONDS = 90 * 24 * 60 * 60; // 90 days

export class FeatureStore {
  async saveFeatureVector(
    userId: string,
    productId: string,
    features: CompleteFeatureVector
  ): Promise<UserFeatureVectorDoc> {
    const container = getRegisteredContainer('user_features');

    const normalizedFeatures = this.normalizeFeatures(features);

    const doc: UserFeatureVectorDoc = {
      id: `fv_${crypto.randomUUID()}`,
      userId,
      productId,
      features,
      normalizedFeatures,
      featureSchemaVersion: FEATURE_SCHEMA_VERSION,
      computedAt: new Date().toISOString(),
      observationWindow: {
        start: features.observationWindow.start.toISOString(),
        end: features.observationWindow.end.toISOString(),
      },
      ttl: DEFAULT_TTL_SECONDS,
    };

    await container.items.create(doc);
    return doc;
  }

  async getLatestFeatureVector(
    userId: string,
    productId: string
  ): Promise<UserFeatureVectorDoc | null> {
    const container = getRegisteredContainer('user_features');

    const query = {
      query:
        'SELECT * FROM c WHERE c.userId = @userId AND c.productId = @productId ORDER BY c.computedAt DESC OFFSET 0 LIMIT 1',
      parameters: [
        { name: '@userId', value: userId },
        { name: '@productId', value: productId },
      ],
    };

    const { resources } = await container.items.query(query).fetchAll();
    return resources[0] || null;
  }

  async getFeatureHistory(
    userId: string,
    productId: string,
    days: number = 30
  ): Promise<UserFeatureVectorDoc[]> {
    const container = getRegisteredContainer('user_features');
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - days);

    const query = {
      query:
        'SELECT * FROM c WHERE c.userId = @userId AND c.productId = @productId AND c.computedAt >= @cutoff ORDER BY c.computedAt DESC',
      parameters: [
        { name: '@userId', value: userId },
        { name: '@productId', value: productId },
        { name: '@cutoff', value: cutoff.toISOString() },
      ],
    };

    const { resources } = await container.items.query(query).fetchAll();
    return resources;
  }

  async getFeaturesForProduct(
    productId: string,
    limit: number = 1000
  ): Promise<UserFeatureVectorDoc[]> {
    const container = getRegisteredContainer('user_features');

    const query = {
      query:
        'SELECT * FROM c WHERE c.productId = @productId ORDER BY c.computedAt DESC OFFSET 0 LIMIT @limit',
      parameters: [
        { name: '@productId', value: productId },
        { name: '@limit', value: limit },
      ],
    };

    const { resources } = await container.items.query(query).fetchAll();
    return resources;
  }

  async computeFeatureStats(
    productId: string
  ): Promise<Record<string, { min: number; max: number; avg: number; std: number }>> {
    const features = await this.getFeaturesForProduct(productId, 10000);

    const stats: Record<string, number[]> = {};

    for (const doc of features) {
      for (const [key, value] of Object.entries(doc.normalizedFeatures)) {
        if (!stats[key]) stats[key] = [];
        stats[key].push(value);
      }
    }

    const result: Record<string, { min: number; max: number; avg: number; std: number }> = {};

    for (const [key, values] of Object.entries(stats)) {
      const min = Math.min(...values);
      const max = Math.max(...values);
      const avg = values.reduce((a, b) => a + b, 0) / values.length;
      const variance = values.reduce((sum, v) => sum + Math.pow(v - avg, 2), 0) / values.length;
      const std = Math.sqrt(variance);

      result[key] = { min, max, avg, std };
    }

    return result;
  }

  normalizeFeatures(features: CompleteFeatureVector): Record<string, number> {
    const normalized: Record<string, number> = {};

    // Behavior features
    normalized.daysSinceLastSession = this.normalizeMinMax(
      features.behavior.daysSinceLastSession,
      0,
      30
    );
    normalized.sessionsLast7Days = this.normalizeMinMax(features.behavior.sessionsLast7Days, 0, 50);
    normalized.sessionsLast30Days = this.normalizeMinMax(
      features.behavior.sessionsLast30Days,
      0,
      200
    );
    normalized.avgSessionDurationMinutes = this.normalizeMinMax(
      features.behavior.avgSessionDurationMinutes,
      0,
      120
    );
    normalized.actionsPerSession = this.normalizeMinMax(features.behavior.actionsPerSession, 0, 50);
    normalized.uniqueFeaturesUsed = this.normalizeMinMax(
      features.behavior.uniqueFeaturesUsed,
      0,
      20
    );
    normalized.sessionFrequencyTrend = this.normalizeRange(
      features.behavior.sessionFrequencyTrend,
      -1,
      1
    );

    // Engagement features
    normalized.featureUsageDiversity = this.normalizeMinMax(
      features.engagement.featureUsageDiversity,
      0,
      1
    );
    normalized.coreActionCompletionRate = this.normalizeMinMax(
      features.engagement.coreActionCompletionRate,
      0,
      1
    );
    normalized.powerUserScore = this.normalizeMinMax(features.engagement.powerUserScore, 0, 1);
    normalized.onboardingCompletionRate = this.normalizeMinMax(
      features.engagement.onboardingCompletionRate,
      0,
      1
    );

    // Performance features
    normalized.errorRateLast7Days = this.normalizeMinMax(
      features.performance.errorRateLast7Days,
      0,
      1
    );
    normalized.errorRateLast30Days = this.normalizeMinMax(
      features.performance.errorRateLast30Days,
      0,
      1
    );
    normalized.avgLatencyMs = this.normalizeMinMax(features.performance.avgLatencyMs, 0, 10000);
    normalized.errorRecoveryRate = this.normalizeMinMax(
      features.performance.errorRecoveryRate,
      0,
      1
    );

    // Social features
    normalized.shareCount = this.normalizeMinMax(features.social.shareCount, 0, 50);
    normalized.inviteCount = this.normalizeMinMax(features.social.inviteCount, 0, 20);
    normalized.collaborationScore = this.normalizeMinMax(features.social.collaborationScore, 0, 1);

    // Revenue features
    normalized.planTier = this.normalizeMinMax(features.revenue.planTier, 0, 2);
    normalized.lifetimeValue = this.normalizeLog(features.revenue.lifetimeValue);
    normalized.upgradeCount = this.normalizeMinMax(features.revenue.upgradeCount, 0, 5);
    normalized.downgradeCount = this.normalizeMinMax(features.revenue.downgradeCount, 0, 3);

    // Rolling features
    normalized.wowSessionChange = this.normalizeRange(features.rolling.wowSessionChange, -1, 1);
    normalized.wowDurationChange = this.normalizeRange(features.rolling.wowDurationChange, -1, 1);
    normalized.cohortSessionPercentile = this.normalizeMinMax(
      features.rolling.cohortSessionPercentile,
      0,
      100
    );
    normalized.cohortEngagementPercentile = this.normalizeMinMax(
      features.rolling.cohortEngagementPercentile,
      0,
      100
    );

    // Product-specific features (if present)
    for (const [key, value] of Object.entries(features.productSpecific)) {
      if (value !== undefined) {
        normalized[key] = typeof value === 'number' ? this.normalizeMinMax(value, 0, 1) : 0;
      }
    }

    return normalized;
  }

  private normalizeMinMax(value: number, min: number, max: number): number {
    if (max === min) return 0;
    return Math.max(0, Math.min(1, (value - min) / (max - min)));
  }

  private normalizeRange(value: number, min: number, max: number): number {
    return this.normalizeMinMax(value, min, max);
  }

  private normalizeLog(value: number): number {
    return Math.log1p(value) / 10; // Normalized log scale
  }
}

export const featureStore = new FeatureStore();
