import { and, asc, eq, inArray, isNull } from 'drizzle-orm';
import { crmQuotations, memberships, users } from '@/db/schema';
import { db } from '@/lib/db';
import { AuthError } from '@/lib/auth/session';
import {
  getApprovalRequest,
  listApprovalRequests
} from '@/features/foundation/approval/server/service';
import type {
  ApprovalDetailRecord,
  ApprovalTimelineItem
} from '@/features/foundation/approval/types';
import {
  getSignaturePositionLookup,
  resolveSignaturePositionLabel
} from '@/features/crm/quotations/document/signature-position';
import type {
  SignatureBlock,
  SignatureParty
} from '@/features/crm/quotations/document/signature.types';

const EMPTY_SIGNATURE_PARTY: SignatureParty = {
  name: '-',
  position: '-',
  actedAt: null,
  userId: null,
  roleCode: null
};

const APPROVED_BY_ROLE_CODES = new Set(['sales_manager', 'department_manager']);

type ApprovalActionWithRole = ApprovalTimelineItem & {
  roleCode: string | null;
};

function toSignatureParty(input?: Partial<SignatureParty> | null): SignatureParty {
  return {
    name: input?.name?.trim() || '-',
    position: input?.position?.trim() || '-',
    actedAt: input?.actedAt ?? null,
    userId: input?.userId ?? null,
    roleCode: input?.roleCode ?? null
  };
}

function mapApprovalActions(detail: ApprovalDetailRecord | null): ApprovalActionWithRole[] {
  if (!detail) {
    return [];
  }

  return detail.timeline
    .filter((item) => item.action === 'approve')
    .map((item) => ({
      ...item,
      roleCode: detail.steps.find((step) => step.stepNumber === item.stepNumber)?.roleCode ?? null
    }));
}

function resolveApprovedByAction(actions: ApprovalActionWithRole[], finalRoleCode: string | null) {
  const candidates = actions.filter(
    (item) =>
      item.roleCode !== finalRoleCode &&
      item.roleCode !== null &&
      APPROVED_BY_ROLE_CODES.has(item.roleCode)
  );

  return candidates.at(-1) ?? null;
}

function resolveAuthorizedByAction(
  actions: ApprovalActionWithRole[],
  finalRoleCode: string | null
) {
  if (!finalRoleCode) {
    return null;
  }

  return actions.filter((item) => item.roleCode === finalRoleCode).at(-1) ?? null;
}

export async function resolveQuotationSignatures(
  quotationId: string,
  organizationId: string
): Promise<SignatureBlock> {
  const [quotation] = await db
    .select({
      id: crmQuotations.id,
      organizationId: crmQuotations.organizationId,
      salesmanId: crmQuotations.salesmanId,
      createdBy: crmQuotations.createdBy,
      createdAt: crmQuotations.createdAt
    })
    .from(crmQuotations)
    .where(
      and(
        eq(crmQuotations.id, quotationId),
        eq(crmQuotations.organizationId, organizationId),
        isNull(crmQuotations.deletedAt)
      )
    )
    .limit(1);

  if (!quotation) {
    throw new AuthError('Quotation not found', 404);
  }

  const approvalRequests = await listApprovalRequests(organizationId, {
    entityType: 'quotation',
    entityId: quotationId,
    limit: 20
  });
  const approvalRequest =
    approvalRequests.items.find((item) => item.status === 'approved') ??
    approvalRequests.items[0] ??
    null;
  const approvalDetail = approvalRequest
    ? await getApprovalRequest(approvalRequest.id, organizationId)
    : null;
  const finalStep = approvalDetail?.steps.at(-1) ?? null;
  const approvalActions = mapApprovalActions(approvalDetail);
  const approvedByAction = resolveApprovedByAction(approvalActions, finalStep?.roleCode ?? null);
  const authorizedByAction = resolveAuthorizedByAction(
    approvalActions,
    finalStep?.roleCode ?? null
  );
  const preparedByUserId = quotation.salesmanId ?? quotation.createdBy;
  const userIds = [
    preparedByUserId,
    approvedByAction?.actedBy ?? null,
    authorizedByAction?.actedBy ?? null
  ].filter((value): value is string => Boolean(value));
  const uniqueUserIds = [...new Set(userIds)];
  const [userRows, membershipRows, positionLookup] = await Promise.all([
    uniqueUserIds.length
      ? db
          .select({ id: users.id, name: users.name })
          .from(users)
          .where(inArray(users.id, uniqueUserIds))
      : Promise.resolve([]),
    uniqueUserIds.length
      ? db
          .select({
            userId: memberships.userId,
            businessRole: memberships.businessRole
          })
          .from(memberships)
          .where(
            and(
              eq(memberships.organizationId, organizationId),
              inArray(memberships.userId, uniqueUserIds)
            )
          )
      : Promise.resolve([]),
    getSignaturePositionLookup(organizationId)
  ]);
  const userMap = new Map(userRows.map((row) => [row.id, row.name]));
  const membershipMap = new Map(membershipRows.map((row) => [row.userId, row.businessRole]));

  const createPartyFromUser = (args: {
    userId: string | null | undefined;
    actedAt?: string | null;
    roleCode?: string | null;
    fallbackName?: string | null;
  }) => {
    if (!args.userId && !args.fallbackName) {
      return toSignatureParty(EMPTY_SIGNATURE_PARTY);
    }

    const businessRole =
      (args.userId ? membershipMap.get(args.userId) : null) ?? args.roleCode ?? null;

    return toSignatureParty({
      name:
        (args.userId ? userMap.get(args.userId) : null) ??
        args.fallbackName?.trim() ??
        EMPTY_SIGNATURE_PARTY.name,
      position: resolveSignaturePositionLabel(businessRole, positionLookup),
      actedAt: args.actedAt ?? null,
      userId: args.userId ?? null,
      roleCode: businessRole
    });
  };

  return {
    preparedBy: createPartyFromUser({
      userId: preparedByUserId,
      actedAt: quotation.createdAt.toISOString()
    }),
    approvedBy: createPartyFromUser({
      userId: approvedByAction?.actedBy,
      actedAt: approvedByAction?.actedAt ?? null,
      roleCode: approvedByAction?.roleCode ?? null,
      fallbackName: approvedByAction?.actorName ?? null
    }),
    authorizedBy: createPartyFromUser({
      userId: authorizedByAction?.actedBy,
      actedAt: authorizedByAction?.actedAt ?? null,
      roleCode: authorizedByAction?.roleCode ?? finalStep?.roleCode ?? null,
      fallbackName: authorizedByAction?.actorName ?? null
    })
  };
}
