import { Injectable } from "@nestjs/common";
import { PrismaService } from "../../prisma/prisma.service";
import { parseWikiLinks, WikiLink } from "../utils/wiki-link-parser";

/**
 * Represents a knowledge entry that matches a link target
 */
export interface ResolvedEntry {
  id: string;
  title: string;
}

/**
 * Represents a resolved wiki link with entry information
 */
export interface ResolvedLink {
  /** The parsed wiki link */
  link: WikiLink;
  /** The resolved entry ID, or null if not found */
  entryId: string | null;
}

/**
 * Represents a backlink - an entry that links to a target entry
 */
export interface Backlink {
  /** The source entry ID */
  sourceId: string;
  /** The source entry title */
  sourceTitle: string;
  /** The source entry slug */
  sourceSlug: string;
  /** The link text used to reference the target */
  linkText: string;
  /** The display text shown for the link */
  displayText: string;
}

/**
 * Service for resolving wiki-style links to knowledge entries
 *
 * Resolution strategy (in order of priority):
 * 1. Exact title match (case-sensitive)
 * 2. Slug match
 * 3. Fuzzy title match (case-insensitive)
 *
 * Supports workspace scoping via RLS
 */
@Injectable()
export class LinkResolutionService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Resolve a single link target to a knowledge entry ID
   *
   * @param workspaceId - The workspace scope
   * @param target - The link target (title or slug)
   * @returns The entry ID if resolved, null if not found or ambiguous
   */
  async resolveLink(workspaceId: string, target: string): Promise<string | null> {
    // Validate input
    if (!target || typeof target !== "string") {
      return null;
    }

    // Trim whitespace
    const trimmedTarget = target.trim();

    // Reject empty or whitespace-only strings
    if (trimmedTarget.length === 0) {
      return null;
    }

    // 1. Try exact title match (case-sensitive)
    const exactMatch = await this.prisma.knowledgeEntry.findFirst({
      where: {
        workspaceId,
        title: trimmedTarget,
      },
      select: {
        id: true,
      },
    });

    if (exactMatch) {
      return exactMatch.id;
    }

    // 2. Try slug match
    const slugMatch = await this.prisma.knowledgeEntry.findUnique({
      where: {
        workspaceId_slug: {
          workspaceId,
          slug: trimmedTarget,
        },
      },
      select: {
        id: true,
      },
    });

    if (slugMatch) {
      return slugMatch.id;
    }

    // 3. Try fuzzy match (case-insensitive)
    const fuzzyMatches = await this.prisma.knowledgeEntry.findMany({
      where: {
        workspaceId,
        title: {
          mode: "insensitive",
          equals: trimmedTarget,
        },
      },
      select: {
        id: true,
        title: true,
      },
    });

    // Return null if no matches or multiple matches (ambiguous)
    if (fuzzyMatches.length === 0) {
      return null;
    }

    if (fuzzyMatches.length > 1) {
      // Ambiguous match - multiple entries with similar titles
      return null;
    }

    // Return the single match
    const match = fuzzyMatches[0];
    return match ? match.id : null;
  }

  /**
   * Resolve multiple link targets in batch
   *
   * @param workspaceId - The workspace scope
   * @param targets - Array of link targets
   * @returns Map of target to resolved entry ID (null if not found)
   */
  async resolveLinks(
    workspaceId: string,
    targets: string[]
  ): Promise<Record<string, string | null>> {
    const result: Record<string, string | null> = {};

    // Deduplicate targets
    const uniqueTargets = Array.from(new Set(targets));

    // Resolve each target
    for (const target of uniqueTargets) {
      const resolved = await this.resolveLink(workspaceId, target);
      result[target] = resolved;
    }

    return result;
  }

  /**
   * Get all entries that could match a link target (for disambiguation UI)
   *
   * @param workspaceId - The workspace scope
   * @param target - The link target
   * @returns Array of matching entries
   */
  async getAmbiguousMatches(workspaceId: string, target: string): Promise<ResolvedEntry[]> {
    const trimmedTarget = target.trim();

    if (trimmedTarget.length === 0) {
      return [];
    }

    const matches = await this.prisma.knowledgeEntry.findMany({
      where: {
        workspaceId,
        title: {
          mode: "insensitive",
          equals: trimmedTarget,
        },
      },
      select: {
        id: true,
        title: true,
      },
    });

    return matches;
  }

  /**
   * Parse wiki links from content and resolve them to knowledge entries
   *
   * @param content - The markdown content containing wiki links
   * @param workspaceId - The workspace scope for resolution
   * @returns Array of resolved links with entry IDs (or null if not found)
   */
  async resolveLinksFromContent(content: string, workspaceId: string): Promise<ResolvedLink[]> {
    // Parse wiki links from content
    const parsedLinks = parseWikiLinks(content);

    if (parsedLinks.length === 0) {
      return [];
    }

    // Resolve each link
    const resolvedLinks: ResolvedLink[] = [];

    for (const link of parsedLinks) {
      const entryId = await this.resolveLink(workspaceId, link.target);
      resolvedLinks.push({
        link,
        entryId,
      });
    }

    return resolvedLinks;
  }

  /**
   * Get all entries that link TO a specific entry (backlinks)
   *
   * @param entryId - The target entry ID
   * @returns Array of backlinks with source entry information
   */
  async getBacklinks(entryId: string): Promise<Backlink[]> {
    // Find all links where this entry is the target
    const links = await this.prisma.knowledgeLink.findMany({
      where: {
        targetId: entryId,
        resolved: true,
      },
      include: {
        source: {
          select: {
            id: true,
            title: true,
            slug: true,
          },
        },
      },
      orderBy: {
        createdAt: "desc",
      },
    });

    return links.map((link) => ({
      sourceId: link.source.id,
      sourceTitle: link.source.title,
      sourceSlug: link.source.slug,
      linkText: link.linkText,
      displayText: link.displayText,
    }));
  }
}
