import type {
  ApprovalActionPayload,
  ApprovalFilters,
  ContactSharePayload,
  CrmDashboardData,
  CrmReferenceData,
  Customer,
  CustomerDetailResponse,
  CustomerFilters,
  CustomerMutationPayload,
  Enquiry,
  EnquiryDetailResponse,
  EnquiryFilters,
  PaginatedResponse,
  Quotation,
  QuotationDetailResponse,
  QuotationFilters
} from './types';
import { initialCrmState, type CrmMockState } from '../data/mock-crm';

function clone<T>(value: T): T {
  return JSON.parse(JSON.stringify(value)) as T;
}

let crmState: CrmMockState = clone(initialCrmState);

function sortItems<T>(items: T[], sort?: string) {
  if (!sort) return items;

  try {
    const parsed = JSON.parse(sort) as Array<{ id: string; desc?: boolean }>;
    const [rule] = parsed;

    if (!rule) return items;

    return [...items].sort((a, b) => {
      const av = String((a as Record<string, unknown>)[rule.id] ?? '');
      const bv = String((b as Record<string, unknown>)[rule.id] ?? '');
      return rule.desc ? bv.localeCompare(av) : av.localeCompare(bv);
    });
  } catch {
    return items;
  }
}

function paginate<T>(items: T[], page = 1, limit = 10): PaginatedResponse<T> {
  const start = (page - 1) * limit;
  return {
    items: items.slice(start, start + limit),
    totalItems: items.length
  };
}

function findBranch(branchId: string) {
  return crmState.branches.find((branch) => branch.id === branchId);
}

function findSalesperson(id: string) {
  return crmState.salespersons.find((sales) => sales.id === id);
}

export async function getCrmReferenceData(): Promise<CrmReferenceData> {
  return clone({
    branches: crmState.branches,
    salespersons: crmState.salespersons,
    masterOptions: crmState.masterOptions,
    sequences: crmState.sequences,
    templates: crmState.templates
  });
}

export async function getCrmDashboardData(): Promise<CrmDashboardData> {
  const openEnquiries = crmState.enquiries.filter((enquiry) =>
    ['new', 'qualifying', 'requirement', 'follow_up'].includes(enquiry.status)
  );
  const pendingQuotations = crmState.quotations.filter(
    (quotation) => quotation.status === 'pending_approval'
  );
  const wonQuotations = crmState.quotations.filter((quotation) => quotation.status === 'accepted');
  const lostQuotations = crmState.quotations.filter((quotation) => quotation.status === 'lost');
  const hotQuotations = crmState.quotations.filter((quotation) => quotation.isHotProject);
  const dueFollowUps = crmState.quotations.flatMap((quotation) => quotation.followUps).slice(0, 5);
  const conversionRate = Math.round(
    (wonQuotations.length / Math.max(1, crmState.enquiries.length)) * 100
  );

  return clone({
    metrics: [
      {
        id: 'm-1',
        label: 'Total Enquiries',
        value: String(crmState.enquiries.length),
        change: '+12%',
        trend: 'up'
      },
      {
        id: 'm-2',
        label: 'Open Enquiries',
        value: String(openEnquiries.length),
        change: '+3',
        trend: 'up'
      },
      {
        id: 'm-3',
        label: 'Pending Quotations',
        value: String(pendingQuotations.length),
        change: '+2',
        trend: 'up'
      },
      {
        id: 'm-4',
        label: 'Pending Approval',
        value: String(crmState.approvals.filter((item) => item.status === 'pending').length),
        change: 'Needs review',
        trend: 'flat'
      },
      {
        id: 'm-5',
        label: 'Won Amount',
        value: `${Math.round(wonQuotations.reduce((sum, item) => sum + item.totalAmount, 0) / 1000000)}M`,
        change: '+18%',
        trend: 'up'
      },
      {
        id: 'm-6',
        label: 'Lost Amount',
        value: `${Math.round(lostQuotations.reduce((sum, item) => sum + item.totalAmount, 0) / 1000000)}M`,
        change: '-6%',
        trend: 'down'
      },
      {
        id: 'm-7',
        label: 'Conversion Rate',
        value: `${conversionRate}%`,
        change: '+4pts',
        trend: 'up'
      },
      {
        id: 'm-8',
        label: 'Hot Projects',
        value: String(hotQuotations.length),
        change: 'Focus',
        trend: 'flat'
      },
      {
        id: 'm-9',
        label: 'Follow-up Due Today',
        value: String(dueFollowUps.length),
        change: 'Today',
        trend: 'flat'
      }
    ],
    enquiryPipeline: [
      { label: 'New', value: crmState.enquiries.filter((item) => item.status === 'new').length },
      {
        label: 'Qualifying',
        value: crmState.enquiries.filter((item) => item.status === 'qualifying').length
      },
      {
        label: 'Requirement',
        value: crmState.enquiries.filter((item) => item.status === 'requirement').length
      },
      {
        label: 'Follow Up',
        value: crmState.enquiries.filter((item) => item.status === 'follow_up').length
      },
      {
        label: 'Converted',
        value: crmState.enquiries.filter((item) => item.status === 'converted').length
      }
    ],
    quotationPipeline: [
      {
        label: 'Draft',
        value: crmState.quotations.filter((item) => item.status === 'draft').length
      },
      {
        label: 'Pending',
        value: crmState.quotations.filter((item) => item.status === 'pending_approval').length
      },
      { label: 'Sent', value: crmState.quotations.filter((item) => item.status === 'sent').length },
      {
        label: 'Accepted',
        value: crmState.quotations.filter((item) => item.status === 'accepted').length
      },
      { label: 'Lost', value: crmState.quotations.filter((item) => item.status === 'lost').length }
    ],
    salesPerformance: crmState.salespersons.map((salesperson) => ({
      label: salesperson.nickname,
      value: crmState.quotations.filter((quotation) => quotation.salesmanId === salesperson.id)
        .length,
      secondaryValue: crmState.quotations
        .filter((quotation) => quotation.salesmanId === salesperson.id)
        .reduce((sum, quotation) => sum + quotation.totalAmount, 0)
    })),
    competitorAnalysis: [
      { label: 'Apex Dock', value: 3 },
      { label: 'LiftPro', value: 2 },
      { label: 'SunNorth', value: 1 },
      { label: 'Others', value: 2 }
    ],
    wonLostTrend: [
      { label: 'Jan', value: 1, secondaryValue: 0 },
      { label: 'Feb', value: 2, secondaryValue: 1 },
      { label: 'Mar', value: 1, secondaryValue: 1 },
      { label: 'Apr', value: 3, secondaryValue: 1 },
      { label: 'May', value: 2, secondaryValue: 2 },
      { label: 'Jun', value: 2, secondaryValue: 1 }
    ],
    pendingApprovals: crmState.approvals.filter((item) => item.status === 'pending'),
    hotQuotations,
    dueFollowUps
  });
}

export async function getCustomers(filters: CustomerFilters): Promise<PaginatedResponse<Customer>> {
  let items = [...crmState.customers];

  if (filters.search) {
    const search = filters.search.toLowerCase();
    items = items.filter(
      (customer) =>
        customer.name.toLowerCase().includes(search) || customer.code.toLowerCase().includes(search)
    );
  }

  if (filters.status) {
    items = items.filter((customer) => customer.customerStatus === filters.status);
  }

  if (filters.branch) {
    items = items.filter((customer) => customer.branchId === filters.branch);
  }

  items = sortItems(items, filters.sort);
  return clone(paginate(items, filters.page, filters.limit));
}

export async function getCustomerById(id: string): Promise<CustomerDetailResponse> {
  const customer = crmState.customers.find((item) => item.id === id);
  if (!customer) {
    throw new Error('Customer not found');
  }

  return clone({
    customer,
    contacts: crmState.contacts.filter((contact) => contact.customerId === id),
    shares: crmState.contactShares.filter((share) =>
      crmState.contacts.some(
        (contact) => contact.id === share.contactId && contact.customerId === id
      )
    ),
    shareLogs: crmState.contactShareLogs.filter((log) =>
      crmState.contacts.some((contact) => contact.id === log.contactId && contact.customerId === id)
    ),
    enquiries: crmState.enquiries.filter((enquiry) => enquiry.customerId === id),
    quotations: crmState.quotations.filter((quotation) =>
      quotation.customerRoles.some((role) => role.customerId === id)
    ),
    auditEvents: crmState.auditEvents.filter((event) => event.entityId === id),
    branch: findBranch(customer.branchId)!
  });
}

export async function getEnquiries(filters: EnquiryFilters): Promise<PaginatedResponse<Enquiry>> {
  let items = [...crmState.enquiries];

  if (filters.search) {
    const search = filters.search.toLowerCase();
    items = items.filter(
      (item) =>
        item.code.toLowerCase().includes(search) || item.title.toLowerCase().includes(search)
    );
  }
  if (filters.status) items = items.filter((item) => item.status === filters.status);
  if (filters.productType) items = items.filter((item) => item.productType === filters.productType);
  if (filters.salesman) items = items.filter((item) => item.salesmanId === filters.salesman);
  if (filters.dateFrom) items = items.filter((item) => item.createdAt >= filters.dateFrom!);
  if (filters.dateTo)
    items = items.filter((item) => item.createdAt <= `${filters.dateTo!}T23:59:59.999Z`);

  items = sortItems(items, filters.sort);
  return clone(paginate(items, filters.page, filters.limit));
}

export async function getEnquiryById(id: string): Promise<EnquiryDetailResponse> {
  const enquiry = crmState.enquiries.find((item) => item.id === id);
  if (!enquiry) {
    throw new Error('Enquiry not found');
  }

  const customer = crmState.customers.find((item) => item.id === enquiry.customerId)!;
  const contact = crmState.contacts.find((item) => item.id === enquiry.contactId)!;
  return clone({
    enquiry,
    customer,
    contact,
    quotations: crmState.quotations.filter((quotation) => quotation.enquiryId === id),
    branch: findBranch(enquiry.branchId)!,
    salesman: findSalesperson(enquiry.salesmanId)!
  });
}

export async function getQuotations(
  filters: QuotationFilters
): Promise<PaginatedResponse<Quotation>> {
  let items = [...crmState.quotations];

  if (filters.search) {
    const search = filters.search.toLowerCase();
    items = items.filter(
      (quotation) =>
        quotation.code.toLowerCase().includes(search) ||
        quotation.project.toLowerCase().includes(search)
    );
  }
  if (filters.status) items = items.filter((quotation) => quotation.status === filters.status);
  if (filters.quotationType)
    items = items.filter((quotation) => quotation.quotationType === filters.quotationType);
  if (filters.salesman)
    items = items.filter((quotation) => quotation.salesmanId === filters.salesman);
  if (filters.hot)
    items = items.filter((quotation) =>
      filters.hot === 'yes' ? quotation.isHotProject : !quotation.isHotProject
    );
  if (filters.dateFrom)
    items = items.filter((quotation) => quotation.quotationDate >= filters.dateFrom!);
  if (filters.dateTo)
    items = items.filter((quotation) => quotation.quotationDate <= filters.dateTo!);

  items = sortItems(items, filters.sort);
  return clone(paginate(items, filters.page, filters.limit));
}

export async function getQuotationById(id: string): Promise<QuotationDetailResponse> {
  const quotation = crmState.quotations.find((item) => item.id === id);
  if (!quotation) {
    throw new Error('Quotation not found');
  }

  const enquiry = crmState.enquiries.find((item) => item.id === quotation.enquiryId)!;
  const customerIds = [...new Set(quotation.customerRoles.map((role) => role.customerId))];
  const contactIds = [
    ...new Set(quotation.customerRoles.map((role) => role.contactId).filter(Boolean))
  ] as string[];

  return clone({
    quotation,
    enquiry,
    customers: crmState.customers.filter((item) => customerIds.includes(item.id)),
    contacts: crmState.contacts.filter((item) => contactIds.includes(item.id)),
    branch: findBranch(quotation.branchId)!,
    salesman: findSalesperson(quotation.salesmanId)!,
    saleAdmin: findSalesperson(quotation.saleAdminId)!,
    auditEvents: crmState.auditEvents.filter((event) => quotation.auditEventIds.includes(event.id))
  });
}

export async function getApprovals(filters: ApprovalFilters) {
  let items = [...crmState.approvals];
  if (filters.branch) items = items.filter((item) => item.branchId === filters.branch);
  if (filters.status) items = items.filter((item) => item.status === filters.status);
  return clone(paginate(items, filters.page, filters.limit));
}

export async function createCustomer(values: CustomerMutationPayload) {
  const customer: Customer = { id: `cus-${Date.now()}`, contactIds: [], ...values };
  crmState.customers.unshift(customer);
  return clone(customer);
}

export async function updateCustomer(id: string, values: CustomerMutationPayload) {
  crmState.customers = crmState.customers.map((customer) =>
    customer.id === id ? { ...customer, ...values } : customer
  );
  return getCustomerById(id);
}

export async function convertEnquiryToQuotation(enquiryId: string) {
  const enquiry = crmState.enquiries.find((item) => item.id === enquiryId);
  if (!enquiry) throw new Error('Enquiry not found');

  const nextId = `qt-${Date.now()}`;
  const newQuotation: Quotation = {
    id: nextId,
    code: `QT2606-${String(crmState.quotations.length + 1).padStart(3, '0')}`,
    enquiryId,
    quotationDate: '2026-06-11',
    validUntil: '2026-07-11',
    quotationType: 'official',
    project: enquiry.title,
    siteLocation: enquiry.projectLocation,
    customerRoles: [
      { role: 'owner', customerId: enquiry.customerId, contactId: enquiry.contactId }
    ],
    salesmanId: enquiry.salesmanId,
    saleAdminId: 'sp-2',
    branchId: enquiry.branchId,
    status: 'draft',
    revision: 0,
    totalAmount: enquiry.expectedValue,
    chancePercent: enquiry.chancePercent,
    isHotProject: enquiry.chancePercent >= 70,
    items: [
      {
        id: `${nextId}-item-1`,
        topic: 'Scope',
        description: enquiry.requirementSummary,
        quantity: 1,
        unit: 'lot',
        unitPrice: enquiry.expectedValue,
        amount: enquiry.expectedValue
      }
    ],
    topics: [
      {
        id: `${nextId}-topic-1`,
        type: 'scope',
        label: 'Scope',
        items: [enquiry.requirementSummary]
      },
      {
        id: `${nextId}-topic-2`,
        type: 'exclusion',
        label: 'Exclusion',
        items: ['To be confirmed']
      },
      { id: `${nextId}-topic-3`, type: 'payment', label: 'Payment', items: ['Pending setup'] }
    ],
    followUps: [],
    attachments: [],
    approvalSteps: [],
    auditEventIds: []
  };

  crmState.quotations.unshift(newQuotation);
  crmState.enquiries = crmState.enquiries.map((item) =>
    item.id === enquiryId
      ? {
          ...item,
          status: 'converted',
          quotationIds: [...item.quotationIds, nextId],
          activities: [
            ...item.activities,
            {
              id: `${enquiryId}-activity-${Date.now()}`,
              type: 'CONVERT',
              title: 'Convert to quotation',
              detail: `สร้าง ${newQuotation.code}`,
              actorName: 'CRM Demo',
              createdAt: '2026-06-11T10:00:00.000Z'
            }
          ]
        }
      : item
  );

  return clone(newQuotation);
}

export async function submitQuotationApproval(quotationId: string) {
  crmState.quotations = crmState.quotations.map((quotation) =>
    quotation.id === quotationId
      ? {
          ...quotation,
          status: 'pending_approval',
          approvalSteps:
            quotation.approvalSteps.length > 0
              ? quotation.approvalSteps
              : [
                  {
                    id: `${quotationId}-ap-1`,
                    level: 1,
                    approverName: 'Sales Director',
                    approverPosition: 'Director',
                    status: 'pending'
                  }
                ]
        }
      : quotation
  );
}

export async function approveQuotation(payload: ApprovalActionPayload) {
  crmState.quotations = crmState.quotations.map((quotation) => {
    if (quotation.id !== payload.quotationId) return quotation;

    const nextSteps: typeof quotation.approvalSteps = quotation.approvalSteps.map((step, index) =>
      index === quotation.approvalSteps.findIndex((item) => item.status === 'pending')
        ? {
            ...step,
            status: 'approved' as const,
            actedAt: '2026-06-11T11:00:00.000Z',
            comment: payload.comment
          }
        : step
    );

    const hasPending = nextSteps.some((step) => step.status === 'pending');
    return {
      ...quotation,
      approvalSteps: nextSteps,
      status: hasPending ? 'pending_approval' : 'approved',
      approvedPdfUrl: hasPending
        ? quotation.approvedPdfUrl
        : `/mock/${quotation.code.toLowerCase()}.pdf`
    };
  });

  crmState.approvals = crmState.approvals.map((item) =>
    item.quotationId === payload.quotationId ? { ...item, status: 'approved' } : item
  );
}

export async function rejectQuotation(payload: ApprovalActionPayload) {
  crmState.quotations = crmState.quotations.map((quotation) =>
    quotation.id === payload.quotationId
      ? {
          ...quotation,
          status: 'rejected' as const,
          approvalSteps: quotation.approvalSteps.map((step, index) =>
            index === quotation.approvalSteps.findIndex((item) => item.status === 'pending')
              ? {
                  ...step,
                  status: 'rejected' as const,
                  actedAt: '2026-06-11T11:30:00.000Z',
                  comment: payload.comment
                }
              : step
          )
        }
      : quotation
  );
  crmState.approvals = crmState.approvals.map((item) =>
    item.quotationId === payload.quotationId ? { ...item, status: 'rejected' } : item
  );
}

export async function createQuotationRevision(quotationId: string) {
  const quotation = crmState.quotations.find((item) => item.id === quotationId);
  if (!quotation) throw new Error('Quotation not found');

  const revisionNumber = quotation.revision + 1;
  const newQuotation: Quotation = {
    ...clone(quotation),
    id: `${quotationId}-rev-${revisionNumber}`,
    code: `${quotation.code}-R${String(revisionNumber).padStart(2, '0')}`,
    revision: revisionNumber,
    status: 'revised',
    totalAmount: quotation.totalAmount + 125000
  };

  crmState.quotations.unshift(newQuotation);
  return clone(newQuotation);
}

export async function markQuotationStatus(quotationId: string, status: Quotation['status']) {
  crmState.quotations = crmState.quotations.map((quotation) =>
    quotation.id === quotationId ? { ...quotation, status } : quotation
  );
}

export async function shareContact(payload: ContactSharePayload) {
  const share = {
    id: `share-${Date.now()}`,
    createdAt: '2026-06-11T12:00:00.000Z',
    ...payload
  };
  crmState.contactShares.unshift(share);
  crmState.contactShareLogs.unshift({
    id: `share-log-${Date.now()}`,
    contactId: payload.contactId,
    action: 'SHARE',
    targetUserName: payload.sharedWithUserName,
    actorName: 'CRM Demo',
    createdAt: '2026-06-11T12:00:00.000Z'
  });
}

export async function revokeContactShare(contactId: string, shareId: string) {
  const current = crmState.contactShares.find((share) => share.id === shareId);
  crmState.contactShares = crmState.contactShares.filter((share) => share.id !== shareId);
  if (current) {
    crmState.contactShareLogs.unshift({
      id: `share-log-${Date.now()}`,
      contactId,
      action: 'REVOKE',
      targetUserName: current.sharedWithUserName,
      actorName: 'CRM Demo',
      createdAt: '2026-06-11T12:30:00.000Z'
    });
  }
}
