Notarial Compliance Checker

<div id="loading-overlay" class="fixed inset-0 bg-gray-100 bg-opacity-75 z-50 flex items-center justify-center">
    <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
    <p class="ml-3 text-lg text-gray-700">Initializing App...</p>
</div>

<div id="app-container" class="max-w-4xl mx-auto opacity-0 transition-opacity duration-500">

    <header class="text-center mb-8 border-b-2 border-blue-200 pb-4">
        <h1 class="text-3xl font-extrabold text-blue-700">Notarial Certificate Compliance Tool</h1>
        <p class="text-gray-600 mt-1">Check a certificate's text against jurisdiction-specific or custom rules.</p>
        <p class="text-sm text-gray-500 mt-2">Current User ID: <span id="user-id-display" class="font-mono text-xs bg-gray-200 p-1 rounded">Loading...</span></p>
    </header>

    <!-- Input Area -->
    <div class="bg-white p-6 rounded-xl shadow-lg mb-8">
        <label for="certificate-text" class="block text-xl font-semibold mb-3 text-gray-700">1. Paste Notarial Certificate Text Here</label>
        <textarea id="certificate-text" rows="8" class="w-full p-4 border-2 border-blue-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition duration-150 text-gray-800" placeholder="e.g., State of California, County of Los Angeles... Personally appeared John Doe..."></textarea>
        <button id="check-button" class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-150 shadow-md hover:shadow-lg focus:outline-none focus:ring-4 focus:ring-blue-300">
            Check Compliance
        </button>
    </div>

    <!-- Compliance Rules and Setup -->
    <div class="grid md:grid-cols-2 gap-6 mb-8">
        <div class="bg-white p-6 rounded-xl shadow-lg">
            <h2 class="text-xl font-semibold text-gray-700 mb-4">2. Current Compliance Rules</h2>
            <div id="rules-list" class="scrollable-list border border-gray-200 rounded-lg p-3 space-y-2">
                <!-- Rules will be dynamically inserted here -->
                <p class="text-center text-gray-500 italic">Loading default rules or custom rules...</p>
            </div>
            <button id="load-custom-rules-button" class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg transition duration-150 focus:outline-none focus:ring-4 focus:ring-green-300">
                Load/Save Custom Rules
            </button>
        </div>

        <!-- Add/Edit Rule Form (Initially Hidden) -->
        <div id="custom-rule-form-container" class="bg-white p-6 rounded-xl shadow-lg hidden">
            <h2 class="text-xl font-semibold text-gray-700 mb-4">Add a Custom Rule</h2>
            <form id="add-rule-form" class="space-y-3">
                <div>
                    <label for="rule-name" class="block text-sm font-medium text-gray-600">Rule Name</label>
                    <input type="text" id="rule-name" required class="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
                </div>
                <div>
                    <label for="rule-keyword" class="block text-sm font-medium text-gray-600">Keyword/Phrase (Case Insensitive)</label>
                    <input type="text" id="rule-keyword" required class="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
                </div>
                <div class="flex items-center space-x-4">
                    <label class="flex items-center">
                        <input type="checkbox" id="rule-required" checked class="form-checkbox text-blue-600 h-5 w-5 rounded">
                        <span class="ml-2 text-sm font-medium text-gray-700">Required Field</span>
                    </label>
                </div>
                <button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg transition duration-150">
                    Add & Save Rule
                </button>
            </form>
            <button id="hide-rule-form-button" class="mt-2 w-full text-sm text-gray-500 hover:text-gray-700 transition duration-150">
                Hide Form
            </button>
        </div>
    </div>

    <!-- Results Area -->
    <div id="results-area" class="bg-white p-6 rounded-xl shadow-2xl border-t-4 border-blue-500 hidden">
        <h2 class="text-2xl font-bold text-gray-800 mb-4">3. Compliance Check Results</h2>
        <div id="compliance-results" class="space-y-3">
            <!-- Results will be dynamically inserted here -->
        </div>
    </div>

</div>

<script type="module">
    import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
    import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
    import { getFirestore, doc, getDoc, addDoc, setDoc, onSnapshot, collection, query, where, getDocs, deleteDoc } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
    import { setLogLevel } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";

    // Global Firebase and App variables
    const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
    const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : null;
    const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;

    let app, db, auth, userId = null;
    let complianceRules = [];

    const defaultRules = [
        { id: 'default-1', ruleName: "Venue (State/County)", keyword: "State of", required: true },
        { id: 'default-2', ruleName: "Date of Notarial Act", keyword: "this [0-31] day of [A-Za-z]+", required: true, isRegex: true },
        { id: 'default-3', ruleName: "Signer's Name", keyword: "Personally appeared", required: true },
        { id: 'default-4', ruleName: "Notary's Signature Line", keyword: "Notary Public", required: true },
        { id: 'default-5', ruleName: "Type of Act", keyword: "(acknowledged|sworn to|affirmed|proved)", required: true },
        { id: 'default-6', ruleName: "Commission Expiration", keyword: "My Commission Expires", required: false }
    ];

    // --- Utility Functions ---

    /**
     * Retries a function with exponential backoff on failure.
     * @param {function} fn - The function to execute.
     * @param {number} maxRetries - Maximum number of retries.
     * @param {number} delay - Initial delay in milliseconds.
     */
    const withRetry = async (fn, maxRetries = 3, delay = 1000) => {
        for (let i = 0; i <= maxRetries; i++) {
            try {
                return await fn();
            } catch (error) {
                if (i === maxRetries) throw error;
                await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
            }
        }
    };

    // --- Core Application Logic ---

    const initializeFirebase = async () => {
        if (!firebaseConfig) {
            console.error("Firebase config not available.");
            document.getElementById('loading-overlay').innerHTML = `<div class="p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg">Firebase Config Missing. Cannot save custom rules.</div>`;
            return;
        }

        try {
            // setLogLevel('Debug');
            app = initializeApp(firebaseConfig);
            db = getFirestore(app);
            auth = getAuth(app);

            // 1. Authenticate
            if (initialAuthToken) {
                await withRetry(() => signInWithCustomToken(auth, initialAuthToken));
            } else {
                await withRetry(() => signInAnonymously(auth));
            }

            // 2. Set up Auth Listener and determine userId
            await new Promise(resolve => {
                const unsubscribe = onAuthStateChanged(auth, (user) => {
                    if (user) {
                        userId = user.uid;
                        console.log("User authenticated:", userId);
                        document.getElementById('user-id-display').textContent = userId;
                        setupEventListeners();
                        loadCustomRules(); // Load rules once authenticated
                        document.getElementById('loading-overlay').classList.add('hidden');
                        document.getElementById('app-container').classList.add('opacity-100');
                    } else {
                        // Should not happen with anonymous/custom token sign-in, but handle case
                        console.error("No user authenticated.");
                    }
                    unsubscribe(); // Unsubscribe after the first state change
                    resolve();
                });
            });

        } catch (error) {
            console.error("Error initializing Firebase or signing in:", error);
            document.getElementById('loading-overlay').innerHTML = `<div class="p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg">Initialization Error. Check console.</div>`;
        }
    };

    const getRulesCollectionRef = () => {
        if (!db || !userId) return null;
        const userDocPath = `/artifacts/${appId}/users/${userId}`;
        return collection(db, userDocPath, 'notary_rules');
    };

    const loadCustomRules = () => {
        const rulesRef = getRulesCollectionRef();
        if (!rulesRef) {
            console.warn("Firestore not ready. Using default rules only.");
            complianceRules = [...defaultRules];
            renderRulesList();
            return;
        }

        // Start real-time listener for rules
        onSnapshot(rulesRef, (snapshot) => {
            const customRules = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));

            // Filter out any default rules that might share the same keyword as a custom rule if needed,
            // but for simplicity, we just add custom rules to the list.
            const updatedRules = [...defaultRules.map(r => ({...r, isDefault: true}))];
            customRules.forEach(cr => {
                const existingDefault = updatedRules.findIndex(r => r.keyword === cr.keyword && r.isDefault);
                if(existingDefault > -1) {
                     // Replace or remove conflicting default rule
                     updatedRules.splice(existingDefault, 1);
                }
                updatedRules.push(cr);
            });

            complianceRules = updatedRules;
            renderRulesList();
        }, (error) => {
            console.error("Error listening to custom rules:", error);
            complianceRules = [...defaultRules];
            renderRulesList();
        });
    };

    const renderRulesList = () => {
        const listContainer = document.getElementById('rules-list');
        listContainer.innerHTML = '';

        if (complianceRules.length === 0) {
            listContainer.innerHTML = `<p class="text-center text-gray-500 italic">No rules defined. Add a custom rule above.</p>`;
            return;
        }

        complianceRules.forEach(rule => {
            const requiredBadge = rule.required ?
                `<span class="text-xs font-medium mr-2 px-2.5 py-0.5 rounded-full bg-red-100 text-red-800">MANDATORY</span>` :
                `<span class="text-xs font-medium mr-2 px-2.5 py-0.5 rounded-full bg-yellow-100 text-yellow-800">OPTIONAL</span>`;

            const typeBadge = rule.isRegex ?
                `<span class="text-xs font-mono px-2.5 py-0.5 rounded bg-purple-100 text-purple-800">REGEX</span>` :
                `<span class="text-xs font-mono px-2.5 py-0.5 rounded bg-teal-100 text-teal-800">PHRASE</span>`;

            const deleteButton = rule.isDefault ? '' :
                `<button data-id="${rule.id}" class="delete-rule-button text-red-500 hover:text-red-700 ml-2" title="Delete Custom Rule">
                    <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
                </button>`;

            const ruleDiv = document.createElement('div');
            ruleDiv.className = 'rule-card flex justify-between items-center bg-gray-50 p-3 rounded-lg border border-gray-200 hover:bg-gray-100';
            ruleDiv.innerHTML = `
                <div class="flex-grow">
                    <p class="font-medium text-gray-800">${rule.ruleName} ${rule.isDefault ? '<span class="text-xs text-blue-500 italic">(Default)</span>' : ''}</p>
                    <p class="text-xs text-gray-500 truncate mt-0.5">Keyword: 
                        <span class="font-mono text-gray-600">${rule.keyword}</span>
                    </p>
                    <div class="mt-1">
                        ${requiredBadge}
                        ${typeBadge}
                    </div>
                </div>
                ${deleteButton}
            `;
            listContainer.appendChild(ruleDiv);
        });

        // Re-attach delete listeners
        document.querySelectorAll('.delete-rule-button').forEach(button => {
            button.addEventListener('click', handleDeleteRule);
        });
    };

    const handleAddRule = async (event) => {
        event.preventDefault();
        const rulesRef = getRulesCollectionRef();
        if (!rulesRef) {
            console.error("Firestore not initialized. Cannot save rule.");
            return;
        }

        const ruleName = document.getElementById('rule-name').value;
        const keyword = document.getElementById('rule-keyword').value;
        const required = document.getElementById('rule-required').checked;

        // Simple check to infer if it's a regex (contains common regex characters)
        const isRegex = /[\\.*?+^$[\](){}|-]/.test(keyword);

        const newRule = {
            ruleName,
            keyword,
            required,
            isRegex,
            createdAt: new Date().toISOString()
        };

        try {
            await withRetry(() => addDoc(rulesRef, newRule));
            document.getElementById('add-rule-form').reset();
        } catch (error) {
            console.error("Error adding rule:", error);
        }
    };

    const handleDeleteRule = async (event) => {
        const button = event.currentTarget;
        const ruleId = button.getAttribute('data-id');
        const rulesRef = getRulesCollectionRef();

        if (!rulesRef || !ruleId) {
            console.error("Cannot delete rule: Firestore not ready or ID missing.");
            return;
        }

        // Create a simple confirmation box (modal is better, but using basic UI text here)
        if (!confirm(`Are you sure you want to delete the rule with ID: ${ruleId}?`)) {
            return;
        }

        try {
            const docRef = doc(rulesRef, ruleId);
            await withRetry(() => deleteDoc(docRef));
            console.log(`Rule ${ruleId} deleted.`);
        } catch (error) {
            console.error("Error deleting rule:", error);
        }
    };

    const checkCompliance = () => {
        const certificateText = document.getElementById('certificate-text').value.toLowerCase();
        const resultsContainer = document.getElementById('compliance-results');
        resultsContainer.innerHTML = '';
        document.getElementById('results-area').classList.remove('hidden');

        let allRequiredPassed = true;

        complianceRules.forEach(rule => {
            let passed = false;
            let matchDetails = '';
            const baseClass = "p-3 rounded-lg shadow-sm flex justify-between items-start";
            let resultClass, iconSvg;

            try {
                if (rule.isRegex) {
                    // Regex check
                    const regex = new RegExp(rule.keyword, 'gi');
                    const match = certificateText.match(regex);
                    if (match && match.length > 0) {
                        passed = true;
                        matchDetails = `Found ${match.length} match(es) for pattern: /${rule.keyword}/`;
                    } else {
                        matchDetails = `Pattern /${rule.keyword}/ not found.`;
                    }
                } else {
                    // Simple phrase check
                    const keywordLower = rule.keyword.toLowerCase();
                    if (certificateText.includes(keywordLower)) {
                        passed = true;
                        matchDetails = `Keyword "${rule.keyword}" found.`;
                    } else {
                        matchDetails = `Keyword "${rule.keyword}" not found.`;
                    }
                }
            } catch (e) {
                console.error(`Error checking rule ${rule.ruleName}:`, e);
                passed = false;
                matchDetails = `ERROR during check. Check keyword format.`;
            }


            if (passed) {
                resultClass = "bg-green-100 border-l-4 border-green-500";
                iconSvg = `<svg class="w-6 h-6 text-green-600 mr-3 mt-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`;
            } else {
                if (rule.required) {
                    allRequiredPassed = false;
                    resultClass = "bg-red-100 border-l-4 border-red-500";
                    iconSvg = `<svg class="w-6 h-6 text-red-600 mr-3 mt-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`;
                } else {
                    resultClass = "bg-yellow-100 border-l-4 border-yellow-500";
                    iconSvg = `<svg class="w-6 h-6 text-yellow-600 mr-3 mt-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>`;
                }
            }

            const resultDiv = document.createElement('div');
            resultDiv.className = `${baseClass} ${resultClass}`;
            resultDiv.innerHTML = `
                <div class="flex">
                    ${iconSvg}
                    <div>
                        <p class="font-semibold text-lg text-gray-900">${rule.ruleName}</p>
                        <p class="text-sm text-gray-700 mt-1">${matchDetails}</p>
                    </div>
                </div>
                <div class="text-right">
                    <span class="text-sm font-bold ${passed ? 'text-green-700' : 'text-red-700'}">${passed ? 'PASS' : 'FAIL'}</span>
                    <p class="text-xs ${rule.required ? 'text-red-600' : 'text-gray-500'}">${rule.required ? 'REQUIRED' : 'OPTIONAL'}</p>
                </div>
            `;
            resultsContainer.appendChild(resultDiv);
        });

        // Overall Summary
        const summaryDiv = document.createElement('div');
        if (allRequiredPassed) {
            summaryDiv.className = "mt-6 p-4 bg-blue-50 border-l-4 border-blue-600 rounded-lg text-lg font-bold text-blue-800";
            summaryDiv.textContent = "OVERALL STATUS: Required elements are PRESENT. Review optional elements.";
        } else {
            summaryDiv.className = "mt-6 p-4 bg-red-50 border-l-4 border-red-600 rounded-lg text-lg font-bold text-red-800";
            summaryDiv.textContent = "OVERALL STATUS: FAILED. One or more REQUIRED elements are MISSING.";
        }
        resultsContainer.prepend(summaryDiv);
    };

    const setupEventListeners = () => {
        document.getElementById('check-button').addEventListener('click', checkCompliance);
        document.getElementById('add-rule-form').addEventListener('submit', handleAddRule);

        const formContainer = document.getElementById('custom-rule-form-container');
        document.getElementById('load-custom-rules-button').addEventListener('click', () => {
            formContainer.classList.toggle('hidden');
        });
        document.getElementById('hide-rule-form-button').addEventListener('click', (e) => {
            e.preventDefault();
            formContainer.classList.add('hidden');
        });
    };

    // Initialize the application on load
    initializeFirebase();

</script>