import { Achievement, AchievementCategory, AchievementProps, AchievementRequirement } from '@core/identity/domain/entities/Achievement';
import { UserAchievement } from '@core/identity/domain/entities/UserAchievement';

import { AchievementOrmEntity } from '../entities/AchievementOrmEntity';
import { UserAchievementOrmEntity } from '../entities/UserAchievementOrmEntity';
import { TypeOrmPersistenceSchemaAdapter } from '../errors/TypeOrmPersistenceSchemaAdapterError';
import {
    assertArray,
    assertBoolean,
    assertDate,
    assertEnumValue,
    assertInteger,
    assertNonEmptyString,
    assertOptionalStringOrNull,
} from '../schema/AchievementSchemaGuard';

const VALID_CATEGORIES = ['driver', 'steward', 'admin', 'community'] as const satisfies readonly AchievementCategory[];
const VALID_RARITIES = ['common', 'uncommon', 'rare', 'epic', 'legendary'] as const;
const VALID_OPERATORS = ['>=', '>', '=', '<', '<='] as const;

export class AchievementOrmMapper {
  toOrmEntity(domain: Achievement): AchievementOrmEntity {
    const entity = new AchievementOrmEntity();
    entity.id = domain.id;
    entity.name = domain.name;
    entity.description = domain.description;
    entity.category = domain.category;
    entity.rarity = domain.rarity;
    entity.iconUrl = domain.iconUrl || null;
    entity.points = domain.points;
    entity.requirements = domain.requirements;
    entity.isSecret = domain.isSecret;
    entity.createdAt = domain.createdAt;
    return entity;
  }

  toDomain(entity: AchievementOrmEntity): Achievement {
    const entityName = 'Achievement';

    // Validate all required fields
    assertNonEmptyString(entityName, 'id', entity.id);
    assertNonEmptyString(entityName, 'name', entity.name);
    assertNonEmptyString(entityName, 'description', entity.description);
    assertEnumValue(entityName, 'category', entity.category, VALID_CATEGORIES);
    assertEnumValue(entityName, 'rarity', entity.rarity, VALID_RARITIES);
    assertOptionalStringOrNull(entityName, 'iconUrl', entity.iconUrl);
    assertInteger(entityName, 'points', entity.points);
    assertArray(entityName, 'requirements', entity.requirements);
    assertBoolean(entityName, 'isSecret', entity.isSecret);
    assertDate(entityName, 'createdAt', entity.createdAt);

    // Validate requirements structure
    for (let i = 0; i < entity.requirements.length; i++) {
      const req = entity.requirements[i] as Record<string, unknown>;
      const reqField = `requirements[${i}]`;
      
      if (!req || typeof req !== 'object') {
        throw new TypeOrmPersistenceSchemaAdapter({
          entityName,
          fieldName: reqField,
          reason: 'invalid_requirement_object',
        });
      }

      assertNonEmptyString(entityName, `${reqField}.type`, req.type as string);
      assertInteger(entityName, `${reqField}.value`, req.value as number);
      assertEnumValue(entityName, `${reqField}.operator`, req.operator as string, VALID_OPERATORS);
    }

    try {
      const createProps: Record<string, unknown> = {
        id: entity.id,
        name: entity.name,
        description: entity.description,
        category: entity.category as AchievementCategory,
        rarity: entity.rarity as Achievement['rarity'],
        points: entity.points,
        requirements: entity.requirements as AchievementRequirement[],
        isSecret: entity.isSecret,
        createdAt: entity.createdAt,
      };

      if (entity.iconUrl !== null) {
        createProps.iconUrl = entity.iconUrl;
      }
      
      return Achievement.create(createProps as unknown as AchievementProps);
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Invalid persisted Achievement';
      throw new TypeOrmPersistenceSchemaAdapter({ entityName, fieldName: 'unknown', reason: 'invalid_shape', message });
    }
  }

  toUserAchievementOrmEntity(domain: UserAchievement): UserAchievementOrmEntity {
    const entity = new UserAchievementOrmEntity();
    entity.id = domain.id;
    entity.userId = domain.userId;
    entity.achievementId = domain.achievementId;
    entity.progress = domain.progress;
    entity.earnedAt = domain.earnedAt;
    entity.notifiedAt = domain.notifiedAt || null;
    return entity;
  }

  toUserAchievementDomain(entity: UserAchievementOrmEntity): UserAchievement {
    const entityName = 'UserAchievement';

    // Validate all required fields
    assertNonEmptyString(entityName, 'id', entity.id);
    assertNonEmptyString(entityName, 'userId', entity.userId);
    assertNonEmptyString(entityName, 'achievementId', entity.achievementId);
    assertInteger(entityName, 'progress', entity.progress);
    assertDate(entityName, 'earnedAt', entity.earnedAt);
    assertOptionalStringOrNull(entityName, 'notifiedAt', entity.notifiedAt);

    try {
      return UserAchievement.create({
        id: entity.id,
        userId: entity.userId,
        achievementId: entity.achievementId,
        progress: entity.progress,
        earnedAt: entity.earnedAt,
        ...(entity.notifiedAt ? { notifiedAt: entity.notifiedAt } : {}),
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Invalid persisted UserAchievement';
      throw new TypeOrmPersistenceSchemaAdapter({ entityName, fieldName: 'unknown', reason: 'invalid_shape', message });
    }
  }
}
