import { and, desc, eq, isNull } from "drizzle-orm";
import { getCache, tenantCacheKey } from "@/core/cache/cache";
import { getDb } from "@/core/db/client";
import { journalPosts } from "@/core/db/schema";
import { normalizeSlug } from "@/core/tenant/slug";
import type { TenantContext } from "@/core/tenant/types";

export type JournalInput = {
  title: string;
  slug?: string;
  excerpt?: string;
  content?: string;
  seoTitle?: string;
  seoDescription?: string;
  status?: "draft" | "published" | "archived";
};

export async function listDashboardJournal(ctx: TenantContext) {
  const db = getDb();
  return db
    .select()
    .from(journalPosts)
    .where(
      and(eq(journalPosts.tenantId, ctx.tenantId), isNull(journalPosts.deletedAt)),
    )
    .orderBy(desc(journalPosts.updatedAt));
}

export async function listPublishedJournal(ctx: TenantContext) {
  const cache = getCache();
  const key = tenantCacheKey(ctx.tenantId, "journal", "published");
  const cached = cache.get<Awaited<ReturnType<typeof fetchPublished>>>(key);
  if (cached) return cached;

  const rows = await fetchPublished(ctx.tenantId);
  cache.set(key, rows, 60);
  return rows;
}

async function fetchPublished(tenantId: string) {
  const db = getDb();
  return db
    .select()
    .from(journalPosts)
    .where(
      and(
        eq(journalPosts.tenantId, tenantId),
        eq(journalPosts.status, "published"),
        isNull(journalPosts.deletedAt),
      ),
    )
    .orderBy(desc(journalPosts.publishedAt));
}

export async function getJournalPostForTenant(
  ctx: TenantContext,
  postId: string,
) {
  const db = getDb();
  const [row] = await db
    .select()
    .from(journalPosts)
    .where(
      and(
        eq(journalPosts.id, postId),
        eq(journalPosts.tenantId, ctx.tenantId),
        isNull(journalPosts.deletedAt),
      ),
    )
    .limit(1);
  return row ?? null;
}

export async function getPublishedJournalBySlug(
  ctx: TenantContext,
  slug: string,
) {
  const db = getDb();
  const [row] = await db
    .select()
    .from(journalPosts)
    .where(
      and(
        eq(journalPosts.tenantId, ctx.tenantId),
        eq(journalPosts.slug, slug),
        eq(journalPosts.status, "published"),
        isNull(journalPosts.deletedAt),
      ),
    )
    .limit(1);
  return row ?? null;
}

export async function createJournalPost(ctx: TenantContext, input: JournalInput) {
  const db = getDb();
  const slug = normalizeSlug(input.slug || input.title);
  const [row] = await db
    .insert(journalPosts)
    .values({
      tenantId: ctx.tenantId,
      title: input.title.trim(),
      slug,
      excerpt: input.excerpt?.trim() || null,
      content: input.content?.trim() || null,
      seoTitle: input.seoTitle?.trim() || null,
      seoDescription: input.seoDescription?.trim() || null,
      status: "draft",
    })
    .returning();

  getCache().invalidateTenant(ctx.tenantId, "journal");
  return row;
}

export async function updateJournalPost(
  ctx: TenantContext,
  postId: string,
  input: Partial<JournalInput>,
) {
  const existing = await getJournalPostForTenant(ctx, postId);
  if (!existing) return null;

  const db = getDb();
  const slug = input.slug
    ? normalizeSlug(input.slug)
    : input.title
      ? normalizeSlug(input.title)
      : undefined;

  const [updated] = await db
    .update(journalPosts)
    .set({
      ...(input.title !== undefined ? { title: input.title.trim() } : {}),
      ...(slug ? { slug } : {}),
      ...(input.excerpt !== undefined
        ? { excerpt: input.excerpt.trim() || null }
        : {}),
      ...(input.content !== undefined
        ? { content: input.content.trim() || null }
        : {}),
      ...(input.seoTitle !== undefined
        ? { seoTitle: input.seoTitle.trim() || null }
        : {}),
      ...(input.seoDescription !== undefined
        ? { seoDescription: input.seoDescription.trim() || null }
        : {}),
      ...(input.status !== undefined
        ? {
            status: input.status,
            publishedAt:
              input.status === "published"
                ? (existing.publishedAt ?? new Date())
                : existing.publishedAt,
          }
        : {}),
      updatedAt: new Date(),
    })
    .where(
      and(
        eq(journalPosts.id, postId),
        eq(journalPosts.tenantId, ctx.tenantId),
      ),
    )
    .returning();

  getCache().invalidateTenant(ctx.tenantId, "journal");
  return updated ?? null;
}

export async function publishJournalPost(ctx: TenantContext, postId: string) {
  return updateJournalPost(ctx, postId, { status: "published" });
}

export async function archiveJournalPost(ctx: TenantContext, postId: string) {
  return updateJournalPost(ctx, postId, { status: "archived" });
}

export async function softDeleteJournalPost(
  ctx: TenantContext,
  postId: string,
) {
  const existing = await getJournalPostForTenant(ctx, postId);
  if (!existing) return null;

  const db = getDb();
  const [updated] = await db
    .update(journalPosts)
    .set({ deletedAt: new Date(), updatedAt: new Date() })
    .where(
      and(
        eq(journalPosts.id, postId),
        eq(journalPosts.tenantId, ctx.tenantId),
      ),
    )
    .returning();

  getCache().invalidateTenant(ctx.tenantId, "journal");
  return updated ?? null;
}
