import { CollectionId } from 'src/modules/cards/domain/value-objects/CollectionId';
import { err, ok, Result } from 'src/shared/core/Result';
import { UseCase } from 'src/shared/core/UseCase';
import {
  ICardQueryRepository,
  CardSortField,
  SortOrder,
  UrlCardView,
} from '../../../domain/ICardQueryRepository';
import { UrlType } from '../../../domain/value-objects/UrlType';
import { ICollectionRepository } from '../../../domain/ICollectionRepository';
import { IProfileService } from '../../../domain/services/IProfileService';
import { IFollowsRepository } from 'src/modules/user/domain/repositories/IFollowsRepository';
import { FollowTargetType } from 'src/modules/user/domain/value-objects/FollowTargetType';
import { ProfileEnricher } from '../../services/ProfileEnricher';

export interface GetCollectionPageQuery {
  collectionId: string;
  callingUserId?: string;
  page?: number;
  limit?: number;
  sortBy?: CardSortField;
  sortOrder?: SortOrder;
  urlType?: UrlType;
}

export type CollectionPageUrlCardDTO = UrlCardView;
export interface GetCollectionPageResult {
  id: string;
  uri?: string;
  name: string;
  description?: string;
  author: {
    id: string;
    name: string;
    handle: string;
    avatarUrl?: string;
  };
  urlCards: CollectionPageUrlCardDTO[];
  cardCount: number;
  followerCount: number;
  pagination: {
    currentPage: number;
    totalPages: number;
    totalCount: number;
    hasMore: boolean;
    limit: number;
  };
  sorting: {
    sortBy: CardSortField;
    sortOrder: SortOrder;
  };
}

export class ValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

export class CollectionNotFoundError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'CollectionNotFoundError';
  }
}

export class GetCollectionPageUseCase
  implements UseCase<GetCollectionPageQuery, Result<GetCollectionPageResult>>
{
  constructor(
    private collectionRepo: ICollectionRepository,
    private cardQueryRepo: ICardQueryRepository,
    private profileService: IProfileService,
    private followsRepository: IFollowsRepository,
  ) {}

  async execute(
    query: GetCollectionPageQuery,
  ): Promise<Result<GetCollectionPageResult>> {
    // Set defaults
    const page = query.page || 1;
    const limit = Math.min(query.limit || 20, 100); // Cap at 100
    const sortBy = query.sortBy || CardSortField.UPDATED_AT;
    const sortOrder = query.sortOrder || SortOrder.DESC;
    const urlType = query.urlType;

    // Validate collection ID
    const collectionIdResult = CollectionId.createFromString(
      query.collectionId,
    );
    if (collectionIdResult.isErr()) {
      return err(new ValidationError('Invalid collection ID'));
    }

    try {
      // Get the collection
      const collectionResult = await this.collectionRepo.findById(
        collectionIdResult.value,
      );

      if (collectionResult.isErr()) {
        return err(
          new Error(
            `Failed to fetch collection: ${collectionResult.error instanceof Error ? collectionResult.error.message : 'Unknown error'}`,
          ),
        );
      }

      const collection = collectionResult.value;
      if (!collection) {
        return err(new CollectionNotFoundError('Collection not found'));
      }

      const collectionPublishedRecordId = collection.publishedRecordId;

      const collectionUri = collectionPublishedRecordId?.uri;

      // Parallelize independent async operations
      const [profileResult, cardsResult, followResult, followerCountResult] =
        await Promise.all([
          // Get author profile
          this.profileService.getProfile(
            collection.authorId.value,
            query.callingUserId,
          ),
          // Get cards in the collection
          this.cardQueryRepo.getCardsInCollection(
            query.collectionId,
            {
              page,
              limit,
              sortBy,
              sortOrder,
              urlType,
            },
            query.callingUserId,
          ),
          // Check if the calling user follows this collection
          query.callingUserId
            ? this.followsRepository.findByFollowerAndTarget(
                query.callingUserId,
                collection.collectionId.getStringValue(),
                FollowTargetType.COLLECTION,
              )
            : Promise.resolve(ok(null)),
          // Get follower count for this collection
          this.followsRepository.getFollowersCount(
            collection.collectionId.getStringValue(),
            FollowTargetType.COLLECTION,
          ),
        ]);

      if (profileResult.isErr()) {
        return err(
          new Error(
            `Failed to fetch author profile: ${profileResult.error instanceof Error ? profileResult.error.message : 'Unknown error'}`,
          ),
        );
      }

      const authorProfile = profileResult.value;

      // Extract unique author IDs from all cards
      const uniqueAuthorIds = Array.from(
        new Set(cardsResult.items.map((card) => card.authorId)),
      );

      // Fetch all author profiles using ProfileEnricher
      const profileEnricher = new ProfileEnricher(this.profileService);
      const authorProfileMapResult = await profileEnricher.buildProfileMap(
        uniqueAuthorIds,
        query.callingUserId,
        {
          skipFailures: true, // Skip cards with failed author profiles
          mapToUser: false, // Use inline profile (without isFollowing)
        },
      );

      if (authorProfileMapResult.isErr()) {
        return err(
          new Error(
            `Failed to fetch card author profiles: ${authorProfileMapResult.error.message}`,
          ),
        );
      }

      const authorProfileMap = authorProfileMapResult.value;

      // Transform raw card data to enriched DTOs with author information
      // Filter out cards with missing author profiles
      const enrichedCards = cardsResult.items
        .map((card) => {
          const author = authorProfileMap.get(card.authorId);
          if (!author) {
            return null; // Skip cards with missing author profiles
          }

          return {
            ...card,
            author,
          };
        })
        .filter((card) => card !== null) as CollectionPageUrlCardDTO[];

      // Process follow status (already fetched in parallel above)
      let isFollowing: boolean | undefined = undefined;
      if (followResult.isOk()) {
        isFollowing = followResult.value !== null;
      }

      // Process follower count (already fetched in parallel above)
      const followerCount = followerCountResult.isOk()
        ? followerCountResult.value
        : 0;

      return ok({
        id: collection.collectionId.getStringValue(),
        uri: collectionUri,
        name: collection.name.value,
        description: collection.description?.value,
        accessType: collection.accessType,
        author: {
          id: authorProfile.id,
          name: authorProfile.name,
          handle: authorProfile.handle,
          avatarUrl: authorProfile.avatarUrl,
          bannerUrl: authorProfile.bannerUrl,
        },
        urlCards: enrichedCards,
        cardCount: collection.cardCount,
        followerCount,
        createdAt: collection.createdAt.toISOString(),
        updatedAt: collection.updatedAt.toISOString(),
        isFollowing,
        pagination: {
          currentPage: page,
          totalPages: Math.ceil(cardsResult.totalCount / limit),
          totalCount: cardsResult.totalCount,
          hasMore: page * limit < cardsResult.totalCount,
          limit,
        },
        sorting: {
          sortBy,
          sortOrder,
        },
      });
    } catch (error) {
      return err(
        new Error(
          `Failed to retrieve collection page: ${error instanceof Error ? error.message : 'Unknown error'}`,
        ),
      );
    }
  }
}
