import {
  type AssignOrCreateIssueInput,
  type AssignScoreToIssueInput,
  assignOrCreateIssueUseCase,
  assignScoreToIssueUseCase,
  type CheckEligibilityInput,
  type CreateIssueFromScoreInput,
  checkEligibilityUseCase,
  createIssueFromScoreUseCase,
  type EmbedScoreFeedbackInput,
  embedScoreFeedbackUseCase,
  IssueDiscoveryLockUnavailableError,
  isEligibilityError,
} from "@domain/issues"
import { type SyncScoreAnalyticsInput, syncScoreAnalyticsUseCase } from "@domain/scores"
import { OrganizationId } from "@domain/shared"
import { withAi } from "@platform/ai"
import { AIGenerateLive } from "@platform/ai-vercel"
import { AIEmbedLive, AIRerankLive } from "@platform/ai-voyage"
import { RedisIssueDiscoveryLockRepositoryLive } from "@platform/cache-redis"
import { ScoreAnalyticsRepositoryLive, withClickHouse } from "@platform/db-clickhouse"
import { IssueRepositoryLive, OutboxEventWriterLive, ScoreRepositoryLive, withPostgres } from "@platform/db-postgres"
import { createLogger, withTracing } from "@repo/observability"
import { Effect, Layer } from "effect"
import { getClickhouseClient, getPostgresClient, getRedisClient } from "../clients.ts"

const logger = createLogger("workflows-issue-discovery")

export const checkEligibility = async (input: CheckEligibilityInput) => {
  try {
    await Effect.runPromise(
      checkEligibilityUseCase(input).pipe(
        withPostgres(ScoreRepositoryLive, getPostgresClient(), OrganizationId(input.organizationId)),
        withTracing,
      ),
    )

    return {
      status: "eligible",
    }
  } catch (error) {
    if (isEligibilityError(error)) {
      return {
        status: "skipped",
        reason: error._tag,
      }
    }

    logger.error("Issue discovery eligibility check failed", {
      scoreId: input.scoreId,
      error,
    })
    throw error
  }
}

export const embedScoreFeedback = async (input: EmbedScoreFeedbackInput) =>
  Effect.runPromise(
    embedScoreFeedbackUseCase(input).pipe(
      withPostgres(ScoreRepositoryLive, getPostgresClient(), OrganizationId(input.organizationId)),
      withAi(AIEmbedLive, getRedisClient()),
      withTracing,
    ),
  )

export const createIssueFromScore = async (input: CreateIssueFromScoreInput) =>
  Effect.runPromise(
    createIssueFromScoreUseCase(input).pipe(
      withPostgres(
        Layer.mergeAll(ScoreRepositoryLive, IssueRepositoryLive),
        getPostgresClient(),
        OrganizationId(input.organizationId),
      ),
      withAi(AIGenerateLive, getRedisClient()),
      withTracing,
    ),
  )

export const assignOrCreateIssue = async (input: AssignOrCreateIssueInput) =>
  Effect.runPromise(
    assignOrCreateIssueUseCase(input).pipe(
      withPostgres(
        Layer.mergeAll(ScoreRepositoryLive, IssueRepositoryLive, OutboxEventWriterLive),
        getPostgresClient(),
        OrganizationId(input.organizationId),
      ),
      Effect.provide(RedisIssueDiscoveryLockRepositoryLive(getRedisClient())),
      // TODO(issue-discovery-rerank): drop AIRerankLive when assignOrCreateIssue
      // relies on Postgres pgvector hybrid search directly.
      withAi(Layer.mergeAll(AIGenerateLive, AIRerankLive), getRedisClient()),
      withTracing,
      Effect.match({
        onFailure: (error) => {
          if (error instanceof IssueDiscoveryLockUnavailableError) {
            return { status: "lock-unavailable" as const }
          }

          throw error
        },
        onSuccess: (result) =>
          result.action === "skipped"
            ? { status: "skipped" as const, reason: result.reason }
            : { status: "serialized" as const, assignment: result },
      }),
    ),
  )

export const assignScoreToIssue = async (input: AssignScoreToIssueInput) =>
  Effect.runPromise(
    assignScoreToIssueUseCase(input).pipe(
      withPostgres(
        Layer.mergeAll(ScoreRepositoryLive, IssueRepositoryLive, OutboxEventWriterLive),
        getPostgresClient(),
        OrganizationId(input.organizationId),
      ),
      Effect.provide(RedisIssueDiscoveryLockRepositoryLive(getRedisClient())),
      withTracing,
      Effect.match({
        onFailure: (error) => {
          if (error instanceof IssueDiscoveryLockUnavailableError) {
            return { status: "lock-unavailable" as const }
          }

          throw error
        },
        onSuccess: (assignment) => ({ status: "assigned" as const, assignment }),
      }),
    ),
  )

export const syncScoreAnalytics = async (input: SyncScoreAnalyticsInput) =>
  Effect.runPromise(
    syncScoreAnalyticsUseCase(input).pipe(
      withPostgres(ScoreRepositoryLive, getPostgresClient(), OrganizationId(input.organizationId)),
      withClickHouse(ScoreAnalyticsRepositoryLive, getClickhouseClient(), OrganizationId(input.organizationId)),
      withTracing,
    ),
  )
