/**
 * Validates a Swedish Personnummer using the Modulo 10 (Luhn) algorithm.
 * Now also verifies that the date component is a valid calendar date.
 */
export function validatePersonnummer(pin: string): boolean {
    const cleanPin = pin.replace(/\D/g, '');

    // 1. Length Check (12 digits: YYYYMMDDXXXX)
    if (cleanPin.length !== 12) return false;

    // 2. Date Check (YYYYMMDD)
    const year = parseInt(cleanPin.substring(0, 4));
    const month = parseInt(cleanPin.substring(4, 6));
    const day = parseInt(cleanPin.substring(6, 8));

    if (month < 1 || month > 12) return false;

    const date = new Date(year, month - 1, day);
    if (
        date.getFullYear() !== year ||
        date.getMonth() !== month - 1 ||
        date.getDate() !== day
    ) {
        return false;
    }

    // 3. Luhn Check (on the last 10 digits)
    const pin10 = cleanPin.substring(2);
    let sum = 0;
    for (let i = 0; i < 10; i++) {
        let val = parseInt(pin10[i]);
        if (i % 2 === 0) {
            val *= 2;
            if (val > 9) val -= 9;
        }
        sum += val;
    }

    return sum % 10 === 0;
}

/**
 * Validates a Swedish address format (Street Name + Number)
 */
export function validateAddress(address: string): boolean {
    // Expects "Streetname 12" or "Street 1B", etc.
    const addressRegex = /^.{2,}\s\d+[a-zA-Z]?.*$/;
    return addressRegex.test(address.trim());
}

/**
 * Fetches Swedish city data for a given 5-digit postcode.
 * Uses the free Zippopotam.us API.
 */
export async function lookupSwedishCity(zipCode: string): Promise<string | null> {
    const cleanZip = zipCode.replace(/\D/g, '');
    if (cleanZip.length !== 5) return null;

    try {
        const response = await fetch(`https://api.zippopotam.us/se/${cleanZip}`);
        if (!response.ok) return null;

        const data = await response.json();
        if (data.places && data.places.length > 0) {
            return data.places[0]['place name'];
        }
        return null;
    } catch (error) {
        console.error('Postcode lookup error:', error);
        return null;
    }
}

/**
 * Normalizes a Swedish address to increase map matching success.
 * Strips apartment numbers, floor info, and standardizes spacing.
 * Example: "Storgatan 12, lgh 1201" -> "Storgatan 12"
 */
export function normalizeSwedishAddress(address: string): string {
    // 1. Remove common Swedish apartment/floor prefixes and everything after them
    // (lgh, vån, trappa, tr, oppgång, etc.)
    let normalized = address.toLowerCase();
    const suffixes = [
        ',', ' lgh', ' vån', ' tr', ' trappa', ' nr', ' uppgång',
        ' floor', ' apt', ' suite', ' bvx', ' box'
    ];

    for (const suffix of suffixes) {
        const index = normalized.indexOf(suffix);
        if (index !== -1) {
            normalized = normalized.substring(0, index);
        }
    }

    // 2. Standardize house numbers (ensure space before letters, e.g. "12A" -> "12 A")
    // Note: Nominatim handles "12A" well, but standardizing can help some older data.
    // However, the most important part is removing the "noise" after the number.

    return normalized.trim();
}

/**
 * Normalizes a Swedish mobile number to the format 467XXXXXXXX.
 * Handles +, 00, 07 prefix and whitespace/hyphens.
 */
export function normalizeSwedishMobile(mobile: string): string {
    // 1. Remove all non-numeric characters
    let clean = mobile.replace(/\D/g, '');

    // 2. Handle international prefixes
    if (clean.startsWith('0046')) {
        clean = clean.substring(2);
    } else if (clean.startsWith('07')) {
        // Change 07... to 467...
        clean = '46' + clean.substring(1);
    }

    // Note: If it already starts with 467, we leave it as is.
    // Minimal length for a Swedish mobile is 46 + 9 digits = 11 digits
    return clean;
}

/**
 * Validates if the string is a valid Swedish mobile number.
 * Allows various formats but normalizes them first.
 */
export function validateSwedishMobile(mobile: string): boolean {
    const normalized = normalizeSwedishMobile(mobile);

    // Swedish mobile numbers (including 46 prefix) are typically 11 digits
    // Format: 46 7X XXX XX XX
    const mobileRegex = /^467\d{8}$/;
    return mobileRegex.test(normalized);
}

/**
 * Verifies if a specific street address exists in a given Swedish city/zip.
 * Uses Nominatim (OpenStreetMap) API with a multi-fallback strategy.
 */
export async function lookupSwedishAddress(street: string, zipCode: string, city: string): Promise<boolean> {
    const cleanZip = zipCode.replace(/\D/g, '');
    const normalizedStreet = normalizeSwedishAddress(street);

    // Helper to call Nominatim with varying parameters
    const fetchFromNominatim = async (s: string, useCity: boolean) => {
        const params: Record<string, string> = {
            street: s,
            postalcode: cleanZip,
            country: 'Sweden',
            format: 'json',
            addressdetails: '1', // Get detailed address info
            limit: '5' // Check a few results in case the first one is a different zip
        };

        if (useCity) params.city = city;

        const urlParams = new URLSearchParams(params);

        try {
            const response = await fetch(`https://nominatim.openstreetmap.org/search?${urlParams.toString()}`, {
                headers: { 'User-Agent': 'NordicStorium-App-Validation' }
            });
            if (!response.ok) return null;
            return await response.json();
        } catch {
            return null;
        }
    };

    interface NominatimResult {
        address?: {
            postcode?: string;
        };
    }

    // Helper to verify that at least one result matches the input zip code
    const hasZipMatch = (data: NominatimResult[]) => {
        if (!data || data.length === 0) return false;
        // Check if any of the results have a matching postcode (handling spaces like "123 45")
        return data.some(item => {
            const resultZip = (item.address?.postcode || '').replace(/\s/g, '');
            return resultZip === cleanZip;
        });
    };

    try {
        // Attempt 1: Normalized Exactly (Street + Number) + Zip
        const try1 = await fetchFromNominatim(normalizedStreet, false);
        if (hasZipMatch(try1)) return true;

        // Attempt 2: Street Name only + Zip
        const streetNameMatch = normalizedStreet.match(/^([^\d]+)/);
        if (streetNameMatch) {
            const streetName = streetNameMatch[1].trim();
            if (streetName.length > 2) {
                const try2 = await fetchFromNominatim(streetName, false);
                if (hasZipMatch(try2)) return true;
            }
        }

        // Attempt 3: Include City (last resort)
        const try3 = await fetchFromNominatim(normalizedStreet, true);
        if (hasZipMatch(try3)) return true;

        return false;
    } catch {
        return true; // Fail open
    }
}
