File size: 3,362 Bytes
5743bc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { buildScanCopilot } from "./ai-copilot";
import type { CheckoutAnalysis } from "./checkout-analysis";

type ScanTrustRating = {
  score?: number;
  tier?: string;
} | null | undefined;

type ScanReportLike = CheckoutAnalysis & {
  domain?: string;
  url?: string;
  [key: string]: any;
};

export function mergeScanReports(base: CheckoutAnalysis, ai: Record<string, any> | null | undefined, domain: string, url: string): ScanReportLike {
  const merged: ScanReportLike = {
    ...base,
    ...ai,
    domain,
    url,
    falseUrgency: {
      ...base.falseUrgency,
      ...ai?.falseUrgency,
    },
    falseScarcity: {
      ...base.falseScarcity,
      ...ai?.falseScarcity,
    },
    confirmShaming: {
      ...base.confirmShaming,
      ...ai?.confirmShaming,
    },
    hiddenFees: {
      ...base.hiddenFees,
      ...ai?.hiddenFees,
      feeItems:
        Array.isArray(ai?.hiddenFees?.feeItems) && ai.hiddenFees.feeItems.length > 0
          ? ai.hiddenFees.feeItems
          : base.hiddenFees?.feeItems ?? [],
    },
    preCheckedAddOns: {
      ...base.preCheckedAddOns,
      ...ai?.preCheckedAddOns,
      addOnLabels:
        Array.isArray(ai?.preCheckedAddOns?.addOnLabels) && ai.preCheckedAddOns.addOnLabels.length > 0
          ? ai.preCheckedAddOns.addOnLabels
          : base.preCheckedAddOns?.addOnLabels ?? [],
    },
    misdirection: {
      ...base.misdirection,
      ...ai?.misdirection,
    },
  };

  const totalPatternsDetected = countDetectedPatterns(merged);

  return {
    ...merged,
    totalPatternsDetected,
    trustScore:
      typeof merged.trustScore === "number"
        ? merged.trustScore
        : Math.max(20, 92 - totalPatternsDetected * 12),
    summary:
      typeof merged.summary === "string" && merged.summary.trim()
        ? merged.summary
        : base.summary,
  };
}

export function countDetectedPatterns(report: ScanReportLike): number {
  return [
    report.falseUrgency?.detected,
    report.falseScarcity?.detected,
    report.confirmShaming?.detected,
    report.hiddenFees?.detected,
    report.preCheckedAddOns?.detected,
    report.misdirection?.detected,
  ].filter(Boolean).length;
}

export function buildScanArtifacts(input: {
  domain: string;
  url: string;
  pageText?: string;
  baseAnalysis: CheckoutAnalysis;
  aiReport?: Record<string, any> | null;
  trustRating?: ScanTrustRating;
}) {
  const darkPatternReport = input.aiReport
    ? mergeScanReports(input.baseAnalysis, input.aiReport, input.domain, input.url)
    : {
        ...input.baseAnalysis,
        domain: input.domain,
        url: input.url,
      };

  const totalPatternsDetected = countDetectedPatterns(darkPatternReport);
  const hiddenFeesTotal = darkPatternReport.hiddenFees?.totalExtra ?? null;
  const trustScore =
    typeof darkPatternReport.trustScore === "number"
      ? darkPatternReport.trustScore
      : input.trustRating?.score ?? 50;

  return {
    darkPatternReport: {
      ...darkPatternReport,
      trustScore,
      totalPatternsDetected,
    },
    totalPatternsDetected,
    hiddenFeesTotal,
    aiCopilot: buildScanCopilot({
      domain: input.domain,
      url: input.url,
      pageText: input.pageText,
      darkPatternReport: {
        ...darkPatternReport,
        trustScore,
        totalPatternsDetected,
      },
      trustRating: input.trustRating,
    }),
  };
}