import { and, asc, count, eq, inArray, isNull, sql } from 'drizzle-orm';
import {
  crmCustomers,
  crmOpportunities,
  crmOpportunityFollowups,
  crmQuotations,
  users
} from '@/db/schema';
import { getUserBranches } from '@/features/foundation/branch-scope/service';
import { getActiveOptionsByCategory } from '@/features/foundation/master-options/service';
import { hasBranchScopeAccess, hasProductScopeAccess } from '@/lib/auth/crm-access';
import { db } from '@/lib/db';
import type { CrmSharedReportFilters } from '../../api/types';
import type {
  ParsedCrmReportFilters,
  PipelineDatasetRecord,
  ResolvedReportContext
} from '../types';

function buildRequiredFilters(filters: CrmSharedReportFilters): ParsedCrmReportFilters {
  return {
    dateFrom: filters.dateFrom ?? null,
    dateTo: filters.dateTo ?? null,
    branch: filters.branch ?? null,
    productType: filters.productType ?? null,
    sales: filters.sales ?? null,
    customer: filters.customer ?? null,
    customerOwner: filters.customerOwner ?? null,
    leadSource: filters.leadSource ?? null,
    pipelineStage: filters.pipelineStage ?? null,
    outcome: filters.outcome ?? null,
    lostReason: filters.lostReason ?? null
  };
}

function getDateRange(filters: ParsedCrmReportFilters) {
  const start = filters.dateFrom ? new Date(`${filters.dateFrom}T00:00:00.000Z`) : null;
  const end = filters.dateTo ? new Date(`${filters.dateTo}T23:59:59.999Z`) : null;

  return { start, end };
}

function matchesDateRange(value: Date, start: Date | null, end: Date | null) {
  if (start && value < start) {
    return false;
  }

  if (end && value > end) {
    return false;
  }

  return true;
}

function isOpenOutcome(stage: string) {
  return stage !== 'closed_won' && stage !== 'closed_lost';
}

function canAccessRecord(
  context: ResolvedReportContext,
  row: {
    branchId: string | null;
    productType: string;
    createdBy: string;
    assignedToUserId: string | null;
  }
) {
  if (!hasBranchScopeAccess(context, row.branchId)) {
    return false;
  }

  if (!hasProductScopeAccess(context, row.productType)) {
    return false;
  }

  if (context.membershipRole === 'admin' || context.ownershipScope !== 'own') {
    return true;
  }

  return row.createdBy === context.userId || row.assignedToUserId === context.userId;
}

export async function listPipelineReportDataset(
  context: ResolvedReportContext,
  filters: CrmSharedReportFilters
): Promise<{
  filters: ParsedCrmReportFilters;
  rows: PipelineDatasetRecord[];
}> {
  const requiredFilters = buildRequiredFilters(filters);
  const { start, end } = getDateRange(requiredFilters);
  const [branches, productTypes, leadSources, opportunityRows, quotationRows, followupRows] =
    await Promise.all([
      getUserBranches(context.organizationId),
      getActiveOptionsByCategory('crm_product_type', { organizationId: context.organizationId }),
      getActiveOptionsByCategory('crm_lead_channel', { organizationId: context.organizationId }),
      db
        .select({
          id: crmOpportunities.id,
          code: crmOpportunities.code,
          customerId: crmOpportunities.customerId,
          branchId: crmOpportunities.branchId,
          productType: crmOpportunities.productType,
          leadChannel: crmOpportunities.leadChannel,
          pipelineStage: crmOpportunities.pipelineStage,
          createdAt: crmOpportunities.createdAt,
          createdBy: crmOpportunities.createdBy,
          assignedToUserId: crmOpportunities.assignedToUserId,
          estimatedValue: crmOpportunities.estimatedValue,
          poAmount: crmOpportunities.poAmount,
          closedWonAt: crmOpportunities.closedWonAt,
          closedLostAt: crmOpportunities.closedLostAt,
          lostReason: crmOpportunities.lostReason,
          customerName: crmCustomers.name,
          customerOwnerUserId: crmCustomers.ownerUserId
        })
        .from(crmOpportunities)
        .innerJoin(crmCustomers, eq(crmCustomers.id, crmOpportunities.customerId))
        .where(
          and(
            eq(crmOpportunities.organizationId, context.organizationId),
            isNull(crmOpportunities.deletedAt),
            eq(crmOpportunities.isActive, true),
            isNull(crmCustomers.deletedAt),
            eq(crmCustomers.isActive, true)
          )
        )
        .orderBy(asc(crmOpportunities.createdAt)),
      db
        .select({
          opportunityId: crmQuotations.opportunityId,
          quotationCount: count(crmQuotations.id),
          quotationValue: sql<number>`coalesce(sum(${crmQuotations.totalAmount}), 0)`
        })
        .from(crmQuotations)
        .where(
          and(
            eq(crmQuotations.organizationId, context.organizationId),
            isNull(crmQuotations.deletedAt),
            eq(crmQuotations.isActive, true),
            sql`${crmQuotations.opportunityId} is not null`
          )
        )
        .groupBy(crmQuotations.opportunityId),
      db
        .select({
          opportunityId: crmOpportunityFollowups.opportunityId,
          lastFollowupDate: sql<Date | null>`max(${crmOpportunityFollowups.followupDate})`
        })
        .from(crmOpportunityFollowups)
        .where(
          and(
            eq(crmOpportunityFollowups.organizationId, context.organizationId),
            isNull(crmOpportunityFollowups.deletedAt)
          )
        )
        .groupBy(crmOpportunityFollowups.opportunityId)
    ]);

  const userIds = [
    ...new Set(
      opportunityRows.flatMap((row) => [
        row.createdBy,
        row.assignedToUserId,
        row.customerOwnerUserId
      ]).filter((value): value is string => Boolean(value))
    )
  ];
  const userRows = userIds.length
    ? await db
        .select({ id: users.id, name: users.name })
        .from(users)
        .where(inArray(users.id, userIds))
    : [];

  const branchLabelMap = new Map(branches.map((item) => [item.id, item.name]));
  const productTypeLabelMap = new Map(productTypes.map((item) => [item.id, item.label]));
  const leadSourceLabelMap = new Map(leadSources.map((item) => [item.id, item.label]));
  const quotationMap = new Map(
    quotationRows
      .filter((row): row is typeof row & { opportunityId: string } => typeof row.opportunityId === 'string')
      .map((row) => [
        row.opportunityId,
        {
          quotationCount: Number(row.quotationCount ?? 0),
          quotationValue: Number(row.quotationValue ?? 0)
        }
      ])
  );
  const followupMap = new Map(followupRows.map((row) => [row.opportunityId, row.lastFollowupDate]));
  const userMap = new Map(userRows.map((row) => [row.id, row.name]));

  const rows = opportunityRows
    .filter((row) => canAccessRecord(context, row))
    .filter((row) => matchesDateRange(row.createdAt, start, end))
    .filter((row) => (requiredFilters.branch ? row.branchId === requiredFilters.branch : true))
    .filter((row) => (requiredFilters.productType ? row.productType === requiredFilters.productType : true))
    .filter((row) => (requiredFilters.sales ? row.assignedToUserId === requiredFilters.sales : true))
    .filter((row) => (requiredFilters.customer ? row.customerId === requiredFilters.customer : true))
    .filter((row) =>
      requiredFilters.customerOwner ? row.customerOwnerUserId === requiredFilters.customerOwner : true
    )
    .filter((row) => (requiredFilters.leadSource ? row.leadChannel === requiredFilters.leadSource : true))
    .filter((row) => (requiredFilters.pipelineStage ? row.pipelineStage === requiredFilters.pipelineStage : true))
    .filter((row) => {
      if (!requiredFilters.outcome) {
        return true;
      }

      if (requiredFilters.outcome === 'won') {
        return row.pipelineStage === 'closed_won';
      }

      if (requiredFilters.outcome === 'lost') {
        return row.pipelineStage === 'closed_lost';
      }

      return isOpenOutcome(row.pipelineStage);
    })
    .filter((row) => (requiredFilters.lostReason ? row.lostReason === requiredFilters.lostReason : true))
    .map<PipelineDatasetRecord>((row) => {
      const quotation = quotationMap.get(row.id);

      return {
        opportunityId: row.id,
        opportunityCode: row.code,
        customerId: row.customerId,
        customerName: row.customerName,
        branchId: row.branchId,
        branchLabel: row.branchId ? (branchLabelMap.get(row.branchId) ?? row.branchId) : '-',
        productTypeId: row.productType,
        productTypeLabel: productTypeLabelMap.get(row.productType) ?? row.productType ?? '-',
        leadSourceId: row.leadChannel,
        leadSourceLabel: row.leadChannel
          ? (leadSourceLabelMap.get(row.leadChannel) ?? row.leadChannel)
          : '-',
        pipelineStage: row.pipelineStage,
        createdAt: row.createdAt,
        createdByUserId: row.createdBy,
        createdByName: userMap.get(row.createdBy) ?? row.createdBy,
        assignedToUserId: row.assignedToUserId,
        assignedToName: row.assignedToUserId ? (userMap.get(row.assignedToUserId) ?? row.assignedToUserId) : null,
        customerOwnerUserId: row.customerOwnerUserId,
        customerOwnerName: row.customerOwnerUserId
          ? (userMap.get(row.customerOwnerUserId) ?? row.customerOwnerUserId)
          : '-',
        quotationCount: quotation?.quotationCount ?? 0,
        quotationValue: quotation?.quotationValue ?? 0,
        estimatedValue: Number(row.estimatedValue ?? 0),
        poAmount: Number(row.poAmount ?? 0),
        lastFollowupDate: followupMap.get(row.id) ?? null,
        closedWonAt: row.closedWonAt,
        closedLostAt: row.closedLostAt,
        lostReason: row.lostReason
      };
    });

  return {
    filters: requiredFilters,
    rows
  };
}
