/**
 * Reference Solution: Advanced GROQ Filtering and Projections
 *
 * Demonstrates:
 * - Conditional projections with select()
 * - Computed/aliased fields
 * - Coalesce for fallback values
 * - Text matching with match operator
 * - count() and defined() functions
 * - Array filtering and mapping
 * - Multi-field ordering
 */

import { createClient } from "@sanity/client"

// --- Sanity client setup ---

const client = createClient({
  projectId: "your-project-id",
  dataset: "production",
  apiVersion: "2024-01-01",
  useCdn: true,
})

// --- Types ---

type ContentItem = {
  _id: string
  _type: "article" | "tutorial" | "changelog"
  title: string
  displayTitle: string
  status: string
  statusLabel: string
  categoryCount: number
  featuredTags: string[]
  publishedAt: string
}

type SearchResult = {
  _id: string
  title: string
  excerpt: string | null
}

type AuthorWithStats = {
  _id: string
  name: string
  postCount: number
  latestPostDate: string | null
}

type ProjectSummary = {
  _id: string
  title: string
  memberCount: number
  activeTasks: string[]
  completionPct: number
}

// --- 1. Conditional projections with select() ---
// Show different labels depending on document status

async function fetchWithConditionalProjections(): Promise<ContentItem[]> {
  const query = `
    *[_type in ["article", "tutorial", "changelog"]] {
      _id,
      _type,
      title,

      // select() returns different values based on conditions
      "statusLabel": select(
        status == "published" => "Live",
        status == "draft"     => "In Progress",
        status == "archived"  => "Archived",
        "Unknown"
      ),

      // Conditional projection based on _type
      "displayTitle": select(
        _type == "changelog"  => "📋 " + title,
        _type == "tutorial"   => "📖 " + title,
        title
      )
    }
  `

  return client.fetch<ContentItem[]>(query)
}

// --- 2. Computed/aliased fields ---
// Rename and compute fields using "alias": expression syntax

async function fetchWithComputedFields(): Promise<AuthorWithStats[]> {
  const query = `
    *[_type == "author"] {
      _id,
      name,

      // Count referencing documents using a sub-query
      "postCount": count(*[_type == "post" && references(^._id)]),

      // Compute the latest post date via a sub-query + order
      "latestPostDate": *[_type == "post" && references(^._id)]
        | order(publishedAt desc)[0].publishedAt,

      // Derive a slug from the name (aliased field)
      "profileUrl": "/authors/" + slug.current
    }
  `

  return client.fetch<AuthorWithStats[]>(query)
}

// --- 3. Coalesce for fallback values ---
// Use coalesce() to pick the first non-null value

async function fetchWithCoalesce(): Promise<SearchResult[]> {
  const query = `
    *[_type == "article"] {
      _id,

      // Prefer seoTitle, fall back to title, then a default string
      "title": coalesce(seoTitle, title, "Untitled"),

      // Prefer metaDescription, fall back to a truncated excerpt
      "excerpt": coalesce(
        metaDescription,
        excerpt,
        pt::text(content)[0...160]
      )
    }
  `

  return client.fetch<SearchResult[]>(query)
}

// --- 4. Text matching with the match operator ---
// Use match for full-text style pattern matching

async function searchByText(term: string): Promise<SearchResult[]> {
  // match supports trailing wildcards for prefix matching
  const query = `
    *[_type == "article" && title match $term] {
      _id,
      title,
      "excerpt": coalesce(excerpt, pt::text(content)[0...160])
    }
  `

  // Pass the search term as a parameter (safe from injection)
  return client.fetch<SearchResult[]>(query, { term: `${term}*` })
}

// Match across multiple fields at once
async function searchMultipleFields(term: string): Promise<SearchResult[]> {
  const query = `
    *[_type == "article" && (title match $term || excerpt match $term)] {
      _id,
      title,
      "excerpt": coalesce(excerpt, null)
    }
  `

  return client.fetch<SearchResult[]>(query, { term: `${term}*` })
}

// --- 5. count() and defined() functions ---
// count() for aggregation, defined() for existence checks

async function fetchDocumentsWithCounts(): Promise<ProjectSummary[]> {
  const query = `
    *[_type == "project" && defined(title)] {
      _id,
      title,

      // count() in a projection — count related members
      "memberCount": count(members),

      // count() with a filtered sub-query
      "openIssueCount": count(*[_type == "issue" && references(^._id) && status != "closed"]),

      // defined() to filter: only include projects that have a deadline
      "hasDeadline": defined(deadline)
    }
  `

  return client.fetch<ProjectSummary[]>(query)
}

// count() in a top-level filter — find documents with at least 3 tags
async function fetchPopularContent(): Promise<ContentItem[]> {
  const query = `
    *[_type == "article" && count(tags) >= 3 && defined(publishedAt)] {
      _id,
      title,
      "tagCount": count(tags),
      publishedAt
    }
  `

  return client.fetch<ContentItem[]>(query)
}

// --- 6. Array filtering and mapping ---
// Filter inside arrays with [condition], project with {}

async function fetchWithArrayOperations(): Promise<ProjectSummary[]> {
  const query = `
    *[_type == "project"] {
      _id,
      title,

      // Filter an array of inline objects — keep only active tasks
      "activeTasks": tasks[status == "active"] {
        title,
        assignee,
        dueDate
      },

      // Compute a completion percentage from the tasks array
      "completionPct": (
        count(tasks[status == "done"]) * 100 /
        select(count(tasks) > 0 => count(tasks), 1)
      ),

      // Map array items to a simpler shape (projection on array)
      "memberNames": members[].name,

      // Combine filter + projection on an array of references
      "recentMilestones": milestones[completed == true] {
        title,
        completedAt
      } | order(completedAt desc)[0...3]
    }
  `

  return client.fetch<ProjectSummary[]>(query)
}

// --- 7. @ (current item in scope) in array filters ---
// Use @ to refer to the current element when filtering scalar arrays

async function fetchWithAtOperator(): Promise<ContentItem[]> {
  const query = `
    *[_type == "article"] {
      _id,
      title,

      // @ refers to each string in the tags array
      "featuredTags": tags[@ in ["featured", "spotlight", "editors-pick"]],

      // @ with match for pattern matching inside scalar arrays
      "techTags": tags[@ match "tech-*"],

      // @ with comparison on an array of numbers
      "highScores": scores[@ > 90],

      // Combine @ filter with count() — number of featured tags
      "featuredTagCount": count(tags[@ in ["featured", "spotlight"]])
    }
  `

  return client.fetch<ContentItem[]>(query)
}

// --- 8. order() and multi-field sorting ---
// Sort by multiple fields with mixed directions

async function fetchOrdered(): Promise<ContentItem[]> {
  // Primary sort by publishedAt descending, secondary by title ascending
  const query = `
    *[_type == "article" && defined(publishedAt)]
      | order(publishedAt desc, title asc)
      [0...20]
    {
      _id,
      title,
      publishedAt,
      "statusLabel": select(
        status == "published" => "Live",
        status == "draft"     => "In Progress",
        "Unknown"
      )
    }
  `

  return client.fetch<ContentItem[]>(query)
}

// order() inside a sub-query for "latest N" patterns
async function fetchAuthorsWithLatestPosts(): Promise<AuthorWithStats[]> {
  const query = `
    *[_type == "author"]
      | order(name asc)
    {
      _id,
      name,
      "postCount": count(*[_type == "post" && references(^._id)]),

      // Sub-query with order() to get the 3 most recent posts
      "recentPosts": *[_type == "post" && references(^._id)]
        | order(publishedAt desc)[0...3]
      {
        _id,
        title,
        publishedAt,
        "categoryCount": count(categories)
      }
    }
  `

  return client.fetch<AuthorWithStats[]>(query)
}

// --- 9. Combining multiple patterns ---
// A realistic query that uses several advanced features together

type DashboardItem = {
  _id: string
  _type: string
  displayTitle: string
  status: string
  tagSummary: string[]
  relatedCount: number
  sortDate: string
}

async function fetchDashboard(): Promise<DashboardItem[]> {
  const query = `
    *[_type in ["article", "tutorial"] && defined(publishedAt)]
      | order(publishedAt desc)
      [0...50]
    {
      _id,
      _type,

      // Coalesce + select for a display-ready title
      "displayTitle": select(
        _type == "tutorial" => "📖 " + coalesce(seoTitle, title),
        coalesce(seoTitle, title)
      ),

      // Conditional status mapping
      "status": select(
        publishedAt < now() && status == "published" => "live",
        status == "draft" => "draft",
        "scheduled"
      ),

      // Array filter with @ + count
      "tagSummary": tags[@ in ["featured", "new", "updated"]],

      // Sub-query count for related documents
      "relatedCount": count(
        *[_type == ^._type && _id != ^._id && count((tags[])[@ in ^.tags[]]) > 0]
      ),

      // Coalesce for sort stability
      "sortDate": coalesce(publishedAt, _createdAt)
    }
  `

  return client.fetch<DashboardItem[]>(query)
}

export {
  fetchAuthorsWithLatestPosts,
  fetchDashboard,
  fetchDocumentsWithCounts,
  fetchOrdered,
  fetchPopularContent,
  fetchWithArrayOperations,
  fetchWithAtOperator,
  fetchWithCoalesce,
  fetchWithComputedFields,
  fetchWithConditionalProjections,
  searchByText,
  searchMultipleFields,
}
