import { CampaignStatus, ConversationPriority, ConversationStatus, RoleCode, TenantStatus } from "@prisma/client";

import { getSession } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

export type DashboardStats = {
  label: string;
  value: string;
  delta: string;
};

export type ConversationRecord = {
  id: string;
  tenantId: string;
  name: string;
  phone: string;
  snippet: string;
  time: string;
  status: "Open" | "Pending" | "Resolved";
  assignee: string;
  channel: string;
  tags: string[];
  priority: "Normal" | "High";
};

export type ContactRecord = {
  id: string;
  tenantId: string;
  fullName: string;
  phone: string;
  email: string;
  tags: string[];
  lastInteraction: string;
  optInStatus: string;
};

export type UserRecord = {
  id: string;
  tenantId: string;
  fullName: string;
  email: string;
  role: "Admin" | "Agent";
  status: "Active" | "Invited";
  lastLogin: string;
  handled: number;
  avgResponse: string;
};

export type CampaignRecord = {
  id: string;
  tenantId: string;
  name: string;
  channel: string;
  audience: string;
  status: "Draft" | "Processing" | "Completed";
  scheduledAt: string;
  delivered: number;
  failed: number;
};

export type TenantRecord = {
  id: string;
  name: string;
  status: "Active" | "Trial" | "Suspended" | "Inactive";
  plan: string;
  channels: string;
  seats: string;
};

function formatRelativeDate(date: Date | null | undefined) {
  if (!date) {
    return "-";
  }

  const now = new Date();
  const diff = now.getTime() - date.getTime();
  const day = 1000 * 60 * 60 * 24;

  if (diff < day) {
    return new Intl.DateTimeFormat("id-ID", {
      hour: "2-digit",
      minute: "2-digit"
    }).format(date);
  }

  if (diff < day * 2) {
    return "Yesterday";
  }

  return new Intl.DateTimeFormat("id-ID", {
    day: "2-digit",
    month: "short",
    year: "numeric"
  }).format(date);
}

function formatDate(date: Date | null | undefined) {
  if (!date) {
    return "-";
  }

  return new Intl.DateTimeFormat("id-ID", {
    day: "2-digit",
    month: "short",
    year: "numeric"
  }).format(date);
}

function titleCase(value: string) {
  return value
    .toLowerCase()
    .split("_")
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(" ");
}

function mapConversationStatus(status: ConversationStatus): ConversationRecord["status"] {
  if (status === ConversationStatus.PENDING) {
    return "Pending";
  }

  if (status === ConversationStatus.RESOLVED) {
    return "Resolved";
  }

  return "Open";
}

function mapConversationPriority(priority: ConversationPriority): ConversationRecord["priority"] {
  return priority === ConversationPriority.HIGH || priority === ConversationPriority.URGENT ? "High" : "Normal";
}

function mapCampaignStatus(status: CampaignStatus): CampaignRecord["status"] {
  if (status === CampaignStatus.COMPLETED) {
    return "Completed";
  }

  if (status === CampaignStatus.PROCESSING || status === CampaignStatus.PARTIAL_FAILED) {
    return "Processing";
  }

  return "Draft";
}

function mapTenantStatus(status: TenantStatus): TenantRecord["status"] {
  switch (status) {
    case TenantStatus.ACTIVE:
      return "Active";
    case TenantStatus.SUSPENDED:
      return "Suspended";
    case TenantStatus.INACTIVE:
      return "Inactive";
    case TenantStatus.TRIAL:
    default:
      return "Trial";
  }
}

async function getTenantScopedWhere() {
  const session = await getSession();

  if (!session?.tenantId || session.role === "super_admin") {
    return {};
  }

  return { tenantId: session.tenantId };
}

export async function getDashboardData() {
  const where = await getTenantScopedWhere();

  const [openConversations, waitingReply, resolvedToday, deliveredMessages, totalOutbound, priorityQueue] =
    await Promise.all([
      prisma.conversation.count({ where: { ...where, status: ConversationStatus.OPEN } }),
      prisma.conversation.count({ where: { ...where, status: ConversationStatus.PENDING } }),
      prisma.conversation.count({
        where: {
          ...where,
          status: ConversationStatus.RESOLVED,
          resolvedAt: {
            gte: new Date(new Date().setHours(0, 0, 0, 0))
          }
        }
      }),
      prisma.message.count({ where: { ...where, direction: "OUTBOUND", deliveryStatus: "DELIVERED" } }),
      prisma.message.count({ where: { ...where, direction: "OUTBOUND" } }),
      prisma.conversation.findMany({
        where,
        orderBy: [{ priority: "desc" }, { lastMessageAt: "desc" }],
        take: 3,
        include: {
          contact: true,
          channel: true,
          assignedUser: true,
          conversationTags: { include: { tag: true } },
          messages: {
            take: 1,
            orderBy: { createdAt: "desc" }
          }
        }
      })
    ]);

  const successRate = totalOutbound > 0 ? `${((deliveredMessages / totalOutbound) * 100).toFixed(1)}%` : "0%";

  return {
    stats: [
      { label: "Open conversations", value: String(openConversations), delta: "Live" },
      { label: "Waiting reply", value: String(waitingReply), delta: "Live" },
      { label: "Resolved today", value: String(resolvedToday), delta: "Today" },
      { label: "Delivery success", value: successRate, delta: "Outbound" }
    ] satisfies DashboardStats[],
    priorityQueue: priorityQueue.map((conversation) => ({
      id: conversation.id,
      tenantId: conversation.tenantId,
      name: conversation.contact.fullName,
      phone: conversation.contact.phoneNumber,
      snippet: conversation.messages[0]?.contentText ?? conversation.subject ?? "No recent message",
      time: formatRelativeDate(conversation.lastMessageAt),
      status: mapConversationStatus(conversation.status),
      assignee: conversation.assignedUser?.fullName ?? "Unassigned",
      channel: conversation.channel.channelName,
      tags: conversation.conversationTags.map((item) => item.tag.name),
      priority: mapConversationPriority(conversation.priority)
    }))
  };
}

export async function getInboxData() {
  const where = await getTenantScopedWhere();

  const conversations = await prisma.conversation.findMany({
    where,
    orderBy: { lastMessageAt: "desc" },
    include: {
      contact: true,
      channel: true,
      assignedUser: true,
      conversationTags: { include: { tag: true } },
      messages: {
        take: 1,
        orderBy: { createdAt: "desc" }
      }
    }
  });

  const mapped = conversations.map((conversation) => ({
    id: conversation.id,
    tenantId: conversation.tenantId,
    name: conversation.contact.fullName,
    phone: conversation.contact.phoneNumber,
    snippet: conversation.messages[0]?.contentText ?? conversation.subject ?? "No recent message",
    time: formatRelativeDate(conversation.lastMessageAt),
    status: mapConversationStatus(conversation.status),
    assignee: conversation.assignedUser?.fullName ?? "Unassigned",
    channel: conversation.channel.channelName,
    tags: conversation.conversationTags.map((item) => item.tag.name),
    priority: mapConversationPriority(conversation.priority)
  })) satisfies ConversationRecord[];

  return {
    conversations: mapped,
    selectedConversation: mapped[0]
  };
}

export async function getContactsData() {
  const where = await getTenantScopedWhere();

  const contacts = await prisma.contact.findMany({
    where,
    orderBy: { lastInteractionAt: "desc" },
    include: {
      contactTags: {
        include: { tag: true }
      }
    }
  });

  return contacts.map((contact) => ({
    id: contact.id,
    tenantId: contact.tenantId,
    fullName: contact.fullName,
    phone: contact.phoneNumber,
    email: contact.email ?? "-",
    tags: contact.contactTags.map((item) => item.tag.name),
    lastInteraction: formatRelativeDate(contact.lastInteractionAt),
    optInStatus: titleCase(contact.optInStatus)
  })) satisfies ContactRecord[];
}

export async function getTeamData() {
  const where = await getTenantScopedWhere();

  const users = await prisma.user.findMany({
    where,
    include: {
      role: true,
      assignedConversations: true
    },
    orderBy: [{ role: { code: "asc" } }, { fullName: "asc" }]
  });

  return users.map((user) => ({
    id: user.id,
    tenantId: user.tenantId,
    fullName: user.fullName,
    email: user.email,
    role: user.role.code === RoleCode.ADMIN_CLIENT ? "Admin" : "Agent",
    status: user.status === "ACTIVE" ? "Active" : "Invited",
    lastLogin: formatRelativeDate(user.lastLoginAt),
    handled: user.assignedConversations.length,
    avgResponse: user.role.code === RoleCode.AGENT ? "3m 12s" : "-"
  })) satisfies UserRecord[];
}

export async function getCampaignsData() {
  const where = await getTenantScopedWhere();

  const campaigns = await prisma.broadcastCampaign.findMany({
    where,
    include: {
      channel: true,
      segment: true
    },
    orderBy: [{ createdAt: "desc" }]
  });

  return campaigns.map((campaign) => ({
    id: campaign.id,
    tenantId: campaign.tenantId,
    name: campaign.name,
    channel: campaign.channel.channelName,
    audience: campaign.segment ? `Segment: ${campaign.segment.name}` : titleCase(campaign.audienceType),
    status: mapCampaignStatus(campaign.status),
    scheduledAt: formatDate(campaign.scheduledAt),
    delivered: campaign.totalDelivered,
    failed: campaign.totalFailed
  })) satisfies CampaignRecord[];
}

export async function getTenantAdminSummary() {
  const where = await getTenantScopedWhere();
  const [contactCount, campaignCount, activeAgents] = await Promise.all([
    prisma.contact.count({ where }),
    prisma.broadcastCampaign.count({ where }),
    prisma.user.count({
      where: {
        ...where,
        role: { code: RoleCode.AGENT }
      }
    })
  ]);

  return {
    contactCount,
    campaignCount,
    activeAgents
  };
}

export async function getPlatformSummary() {
  const [tenantCount, connectedChannels, failedWebhooks, tenants] = await Promise.all([
    prisma.tenant.count(),
    prisma.channel.count({ where: { status: "CONNECTED" } }),
    prisma.webhookEvent.count({ where: { processStatus: "failed" } }),
    prisma.tenant.findMany({
      include: {
        plan: true,
        channels: true,
        users: true
      },
      orderBy: { createdAt: "asc" }
    })
  ]);

  return {
    stats: [
      { label: "Active tenants", value: String(tenantCount), delta: "Global" },
      { label: "Connected channels", value: String(connectedChannels), delta: "Healthy" },
      { label: "Webhook failures", value: String(failedWebhooks), delta: "Watch" },
      { label: "Tracked seats", value: String(tenants.reduce((sum, tenant) => sum + tenant.users.length, 0)), delta: "Users" }
    ] satisfies DashboardStats[],
    tenants: tenants.map((tenant) => ({
      id: tenant.id,
      name: tenant.name,
      status: mapTenantStatus(tenant.status),
      plan: tenant.plan.name,
      channels: `${tenant.channels.filter((channel) => channel.status === "CONNECTED").length} connected`,
      seats: `${tenant.users.length}/${tenant.plan.seatQuota}`
    })) satisfies TenantRecord[]
  };
}

export async function getTenantsData() {
  const tenants = await prisma.tenant.findMany({
    include: {
      plan: true,
      channels: true,
      users: true
    },
    orderBy: { createdAt: "asc" }
  });

  return tenants.map((tenant) => ({
    id: tenant.id,
    name: tenant.name,
    status: mapTenantStatus(tenant.status),
    plan: tenant.plan.name,
    channels: `${tenant.channels.filter((channel) => channel.status === "CONNECTED").length} connected`,
    seats: `${tenant.users.length}/${tenant.plan.seatQuota}`
  })) satisfies TenantRecord[];
}
