Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
FeatureKeyMapper
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
9 / 9
9
100.00% covered (success)
100.00%
1 / 1
 toNewKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toLegacyKey
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAllMappings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasLegacyKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasNewKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAlignmentDirection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAlignmentKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAlignmentKeys
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUniqueNewKeys
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Services;
4
5/**
6 * Service for mapping legacy feature keys to new snake_case keys.
7 *
8 * This service provides bidirectional mapping between legacy feature keys
9 * (e.g., 'Bold', 'Number of Characters that can be used per flycut') and
10 * the new standardized snake_case keys (e.g., 'bold', 'character_limit').
11 *
12 * Used for backward compatibility during the transition from legacy plan
13 * features to the new database-driven Plan/Feature/PlanFeature structure.
14 */
15class FeatureKeyMapper
16{
17    /**
18     * Mapping from legacy keys to new keys.
19     *
20     * @var array<string, string>
21     */
22    private const LEGACY_TO_NEW = [
23        // Formatting features
24        'Bold' => 'bold',
25        'Italic' => 'italic',
26        'Underline' => 'underline',
27        'Strikethrough' => 'strikethrough',
28        'Hyperlink' => 'hyperlink',
29        'Font Size' => 'font_size',
30        'Fonts' => 'font_family',
31        'Font Color' => 'font_color',
32        'Background Color' => 'background_color',
33        'Bullet Points' => 'bullet_points',
34        'Numbered List' => 'numbered_list',
35        'Increase Indent' => 'indent',
36        'Blocks (Headings)' => 'blocks',
37        'Clear Formatting' => 'clear_formatting',
38        'Preview' => 'preview',
39        'Emoticons' => 'emoticons',
40        'Images' => 'images',
41        'Giphy' => 'giphys',
42        'Embed Videos' => 'embed_videos',
43
44        // Alignment features (legacy had separate keys)
45        'Alignment - Left' => 'alignment',
46        'Alignment - Centered' => 'alignment',
47        'Alignment - Right' => 'alignment',
48        'Alignment - Left/Centered/Right' => 'alignment',
49
50        // Limits features
51        'Number of Characters that can be used per flycut' => 'character_limit',
52        'Number of FlyCuts that can be created' => 'shortcuts',
53        'Categories' => 'categories_count',
54        'Subcategory' => 'subcategories_count',
55        'FlyPlates' => 'templates',
56        'ShortcutVersionRollBack' => 'version_history_limit',
57
58        // General features
59        'Search Bar' => 'search_bar',
60        'Latest FlyCuts' => 'latest_flycuts',
61
62        // Storage features
63        'Storage Limits' => 'storage_limits',
64        'Rich Text Editor' => 'rich_text_editor',
65        'Image Upload Size' => 'image_upload_size',
66        'Media Storage' => 'media_storage',
67
68        // AI features (these are typically plan-level attributes)
69        'prompts_per_day' => 'prompts_per_day',
70        'flycut_deployment' => 'flycut_deployment',
71        'flygrammar_actions' => 'flygrammar_actions',
72        'user_custom_prompts' => 'user_custom_prompts',
73        'user_persona_available' => 'user_persona_available',
74        'regenerate_count' => 'regenerate_count',
75        'has_fly_learning' => 'has_fly_learning',
76        'can_disable_flygrammar' => 'can_disable_flygrammar',
77    ];
78
79    /**
80     * Alignment directions for mapping legacy alignment keys.
81     *
82     * @var array<string, string>
83     */
84    private const ALIGNMENT_DIRECTION_MAP = [
85        'Alignment - Left' => 'left',
86        'Alignment - Centered' => 'center',
87        'Alignment - Right' => 'right',
88    ];
89
90    /**
91     * Convert a legacy feature key to a new snake_case key.
92     *
93     * @param  string  $legacyKey  The legacy feature key
94     * @return string The new snake_case key, or the original key if no mapping exists
95     */
96    public function toNewKey(string $legacyKey): string
97    {
98        return self::LEGACY_TO_NEW[$legacyKey] ?? $legacyKey;
99    }
100
101    /**
102     * Convert a new snake_case key to a legacy key.
103     *
104     * Note: For alignment, this returns the generic 'Alignment - Left/Centered/Right' key.
105     *
106     * @param  string  $newKey  The new snake_case key
107     * @return string The legacy key, or the original key if no mapping exists
108     */
109    public function toLegacyKey(string $newKey): string
110    {
111        $newToLegacy = array_flip(self::LEGACY_TO_NEW);
112
113        return $newToLegacy[$newKey] ?? $newKey;
114    }
115
116    /**
117     * Get all key mappings from legacy to new.
118     *
119     * @return array<string, string>
120     */
121    public function getAllMappings(): array
122    {
123        return self::LEGACY_TO_NEW;
124    }
125
126    /**
127     * Check if a legacy key exists in the mapping.
128     *
129     * @param  string  $legacyKey  The legacy key to check
130     * @return bool True if the key exists in the mapping
131     */
132    public function hasLegacyKey(string $legacyKey): bool
133    {
134        return array_key_exists($legacyKey, self::LEGACY_TO_NEW);
135    }
136
137    /**
138     * Check if a new key exists in the mapping.
139     *
140     * @param  string  $newKey  The new key to check
141     * @return bool True if the key exists in the mapping
142     */
143    public function hasNewKey(string $newKey): bool
144    {
145        return in_array($newKey, self::LEGACY_TO_NEW, true);
146    }
147
148    /**
149     * Get the alignment direction from a legacy alignment key.
150     *
151     * @param  string  $legacyKey  The legacy alignment key (e.g., 'Alignment - Left')
152     * @return string|null The direction ('left', 'center', 'right') or null if not an alignment key
153     */
154    public function getAlignmentDirection(string $legacyKey): ?string
155    {
156        return self::ALIGNMENT_DIRECTION_MAP[$legacyKey] ?? null;
157    }
158
159    /**
160     * Check if a legacy key is an alignment-specific key.
161     *
162     * @param  string  $legacyKey  The legacy key to check
163     * @return bool True if the key is an alignment-specific key
164     */
165    public function isAlignmentKey(string $legacyKey): bool
166    {
167        return array_key_exists($legacyKey, self::ALIGNMENT_DIRECTION_MAP);
168    }
169
170    /**
171     * Get all legacy alignment keys.
172     *
173     * @return array<string>
174     */
175    public function getAlignmentKeys(): array
176    {
177        return array_keys(self::ALIGNMENT_DIRECTION_MAP);
178    }
179
180    /**
181     * Get all unique new keys (without duplicates for alignment).
182     *
183     * @return array<string>
184     */
185    public function getUniqueNewKeys(): array
186    {
187        return array_unique(array_values(self::LEGACY_TO_NEW));
188    }
189}