/**
 * This file is part of Radicale Server - Calendar Server
 * Copyright © 2026-2026 Max Berger <max@berger.name>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import { COLOR_RE } from "../constants.js";
import { ErrorHandler } from "./error.js";
import { isValidHREF } from "./misc.js";

/**
 * Manages form validation by running validation functions on input fields.
 */
export class FormValidator {

    /**
     * @param {ErrorHandler} error_handler
     */
    constructor(error_handler) {
        this.error_handler = error_handler;
        /** @type {Array<{field: HTMLInputElement, validation_method: function(): ?string}>} */
        this.validation_methods = [];
    }

    /**
     * @param {HTMLInputElement} field
     * @param {function(): ?string} validation_method
     */
    addValidator(field, validation_method) {
        this.validation_methods.push({ field, validation_method });
        field.addEventListener("input", () => {
            this.validate();
        });
        this.validate();
    }

    /**
     * Validates all added validators.
     * @returns true if all validators are valid
     */
    validate() {
        let errorMessages = [];
        for (let { validation_method } of this.validation_methods) {
            let errorMessage = validation_method();
            if (errorMessage) {
                errorMessages.push(errorMessage);
            }
        }
        this.error_handler.setErrors(errorMessages);
        return errorMessages.length === 0;
    }
}

/**
 * Validates that the input is not empty.
 * @param {HTMLInputElement} input 
 * @param {string} field_name 
 * @returns{function(): ?string}
 */
export function validate_non_empty(input, field_name) {
    return () => {
        if (input.value) {
            return null;
        }
        return field_name + " is empty";
    };
}

/**
 * Validates that the input is not empty and not equal to a target string.
 * @param {HTMLInputElement} input
 * @param {string} target
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_not_empty_or_equals(input, target, field_name) {
    return () => {
        let value = input.value.trim();
        if (!value) {
            return field_name + " is empty";
        }
        if (value === target) {
            return field_name + " cannot be " + target;
        }
        return null;
    };
}

/**
 * Validates that the input is a valid HREF.
 * @param {HTMLInputElement} input
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_href(input, field_name) {
    return () => {
        let value = input.value.trim();
        if (!value) {
            return field_name + " is empty";
        }
        if (value.startsWith("/")) {
            return field_name + " cannot start with /";
        }
        if (!isValidHREF(value)) {
            return field_name + " is invalid";
        }
        return null;
    };
}

/**
 * Validates that the input is a valid color.
 * @param {HTMLInputElement} input
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_color(input, field_name) {
    return () => {
        let value = input.value.trim();
        if (!value) {
            return null;
        }
        if (!COLOR_RE.exec(value)) {
            return field_name + " is invalid";
        }
        return null;
    };
}

/**
 * Validates that the input matches a specific string.
 * @param {HTMLInputElement} input
 * @param {string} target
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_equals(input, target, field_name) {
    return () => {
        let value = input.value;
        if (value === target) {
            return null;
        }
        return "Please type " + target + " in the " + field_name + " field";
    };
}

/**
 * Validates that at least one file is selected in a file input.
 * @param {HTMLInputElement} input
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_files(input, field_name) {
    return () => {
        if (input.files && input.files.length > 0) {
            return null;
        }
        return "Please select at least one " + field_name;
    };
}

/**
 * Validates that the input is a valid integer (if not empty).
 * @param {HTMLInputElement} input
 * @param {string} field_name
 * @returns {function(): ?string}
 */
export function validate_integer(input, field_name) {
    return () => {
        let value = input.value.trim();
        if (!value) {
            return null;
        }
        let parsed = parseInt(value, 10);
        if (isNaN(parsed) || String(parsed) !== value) {
            return field_name + " must be an integer";
        }
        return null;
    };
}
