/*
 * SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
 * SPDX-License-Identifier: AGPL-3.0-only
 */

package form

import (
	"context"
	"errors"
	"net/mail"
	"net/url"
	"regexp"

	"dev.tandem.ws/tandem/camper/pkg/database"
	"dev.tandem.ws/tandem/camper/pkg/locale"
)

type Validator struct {
	l     *locale.Locale
	AllOK bool
}

func NewValidator(l *locale.Locale) *Validator {
	return &Validator{
		l:     l,
		AllOK: true,
	}
}

func (v *Validator) CheckRequired(input *Input, message string) bool {
	return v.check(input, input.Val != "", message)
}

func (v *Validator) CheckMinLength(input *Input, min int, message string) bool {
	return v.check(input, len(input.Val) >= min, message)
}

func (v *Validator) CheckValidEmail(input *Input, message string) bool {
	_, err := mail.ParseAddress(input.Val)
	return v.check(input, err == nil, message)
}

func (v *Validator) CheckValidURL(input *Input, message string) bool {
	_, err := url.Parse(input.Val)
	return v.check(input, err == nil, message)
}

func (v *Validator) CheckValidVATIN(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
	b, err := conn.GetBool(ctx, "select input_is_valid($1 || $2, 'vatin')", country, input.Val)
	if err != nil {
		return false, err
	}
	return v.check(input, b, message), nil
}

func (v *Validator) CheckValidPhone(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
	b, err := conn.GetBool(ctx, "select input_is_valid_phone($1, $2)", input.Val, country)
	if err != nil {
		return false, err
	}
	return v.check(input, b, message), nil
}

func (v *Validator) CheckValidColor(ctx context.Context, conn *database.Conn, input *Input, message string) (bool, error) {
	b, err := conn.GetBool(ctx, "select input_is_valid($1, 'color')", input.Val)
	if err != nil {
		return false, err
	}
	return v.check(input, b, message), nil
}

func (v *Validator) CheckValidPostalCode(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
	pattern, err := conn.GetText(ctx, "select '^' || postal_code_regex || '$' from country where country_code = $1", country)
	if err != nil {
		return false, err
	}
	match, err := regexp.MatchString(pattern, input.Val)
	if err != nil {
		return false, err
	}
	return v.check(input, match, message), nil
}

func (v *Validator) CheckPasswordConfirmation(password *Input, confirm *Input, message string) bool {
	return v.check(confirm, password.Val == confirm.Val, message)
}

func (v *Validator) CheckSelectedOptions(field *Select, message string) bool {
	return v.check(field, field.validOptionsSelected(), message)
}

func (v *Validator) CheckImageFile(field *File, message string) bool {
	return v.check(field, field.ContentType == "image/png" || field.ContentType == "image/jpeg", message)
}

type field interface {
	setError(error)
}

func (v *Validator) check(field field, ok bool, message string) bool {
	if !ok {
		field.setError(errors.New(v.l.Get(message)))
		v.AllOK = false
	}
	return ok
}
