import re

import pytz
from flask_login import current_user
from flask_wtf import Form
from datetime import datetime, timedelta
from notifications_utils.recipients import (
    validate_phone_number,
    InvalidPhoneError
)
from wtforms import (
    validators,
    StringField,
    PasswordField,
    ValidationError,
    TextAreaField,
    FileField,
    BooleanField,
    HiddenField,
    IntegerField,
    RadioField,
    FieldList,
    DateField,
    SelectField)
from wtforms.fields.html5 import EmailField, TelField, SearchField
from wtforms.validators import (DataRequired, Email, Length, Regexp, Optional)

from app.main.validators import (Blacklist, CsvFileValidator, ValidGovEmail, NoCommasInPlaceHolders, OnlyGSMCharacters)


def get_time_value_and_label(future_time):
    return (
        future_time.replace(tzinfo=None).isoformat(),
        '{} at {}'.format(
            get_human_day(future_time.astimezone(pytz.timezone('Europe/London'))),
            get_human_time(future_time.astimezone(pytz.timezone('Europe/London')))
        )
    )


def get_human_time(time):
    return {
        '0': 'midnight',
        '12': 'midday'
    }.get(
        time.strftime('%-H'),
        time.strftime('%-I%p').lower()
    )


def get_human_day(time, prefix_today_with='T'):
    #  Add 1 hour to get ‘midnight today’ instead of ‘midnight tomorrow’
    time = (time - timedelta(hours=1)).strftime('%A')
    if time == datetime.utcnow().strftime('%A'):
        return '{}oday'.format(prefix_today_with)
    if time == (datetime.utcnow() + timedelta(days=1)).strftime('%A'):
        return 'Tomorrow'
    return time


def get_furthest_possible_scheduled_time():
    return (datetime.utcnow() + timedelta(days=4)).replace(hour=0)


def get_next_hours_until(until):
    now = datetime.utcnow()
    hours = int((until - now).total_seconds() / (60 * 60))
    return [
        (now + timedelta(hours=i)).replace(minute=0, second=0).replace(tzinfo=pytz.utc)
        for i in range(1, hours + 1)
    ]


def get_next_days_until(until):
    now = datetime.utcnow()
    days = int((until - now).total_seconds() / (60 * 60 * 24))
    return [
        get_human_day(
            (now + timedelta(days=i)).replace(tzinfo=pytz.utc),
            prefix_today_with='Later t'
        )
        for i in range(0, days + 1)
    ]


def email_address(label='Email address', gov_user=True):
    validators = [
        Length(min=5, max=255),
        DataRequired(message='Can’t be empty'),
        Email(message='Enter a valid email address')
    ]

    if gov_user:
        validators.append(ValidGovEmail())
    return EmailField(label, validators)


class UKMobileNumber(TelField):
    def pre_validate(self, form):
        try:
            validate_phone_number(self.data)
        except InvalidPhoneError as e:
            raise ValidationError(str(e))


def mobile_number():
    return UKMobileNumber('Mobile number',
                          validators=[DataRequired(message='Can’t be empty')])


def password(label='Password'):
    return PasswordField(label,
                         validators=[DataRequired(message='Can’t be empty'),
                                     Length(8, 255, message='Must be at least 8 characters'),
                                     Blacklist(message='Choose a password that’s harder to guess')])


def sms_code():
    verify_code = '^\d{5}$'
    return StringField('Text message code',
                       validators=[DataRequired(message='Can’t be empty'),
                                   Regexp(regex=verify_code,
                                          message='Code not found')])


class LoginForm(Form):
    email_address = StringField('Email address', validators=[
        Length(min=5, max=255),
        DataRequired(message='Can’t be empty'),
        Email(message='Enter a valid email address')
    ])
    password = PasswordField('Password', validators=[
        DataRequired(message='Enter your password')
    ])


class RegisterUserForm(Form):
    name = StringField('Full name',
                       validators=[DataRequired(message='Can’t be empty')])
    email_address = email_address()
    mobile_number = mobile_number()
    password = password()


class RegisterUserFromInviteForm(Form):
    name = StringField('Full name',
                       validators=[DataRequired(message='Can’t be empty')])
    mobile_number = mobile_number()
    password = password()
    service = HiddenField('service')
    email_address = HiddenField('email_address')


class PermissionsForm(Form):
    send_messages = BooleanField("Send messages from existing templates")
    manage_service = BooleanField("Modify this service, its team, and its templates")
    manage_api_keys = BooleanField("Create and revoke API keys")


class InviteUserForm(PermissionsForm):
    email_address = email_address(gov_user=False)

    def __init__(self, invalid_email_address, *args, **kwargs):
        super(InviteUserForm, self).__init__(*args, **kwargs)
        self.invalid_email_address = invalid_email_address.lower()

    def validate_email_address(self, field):
        if field.data.lower() == self.invalid_email_address:
            raise ValidationError("You can’t send an invitation to yourself")


class TwoFactorForm(Form):
    def __init__(self, validate_code_func, *args, **kwargs):
        '''
        Keyword arguments:
        validate_code_func -- Validates the code with the API.
        '''
        self.validate_code_func = validate_code_func
        super(TwoFactorForm, self).__init__(*args, **kwargs)

    sms_code = sms_code()

    def validate_sms_code(self, field):
        is_valid, reason = self.validate_code_func(field.data)
        if not is_valid:
            raise ValidationError(reason)


class EmailNotReceivedForm(Form):
    email_address = email_address()


class TextNotReceivedForm(Form):
    mobile_number = mobile_number()


class AddServiceForm(Form):
    def __init__(self, names_func, *args, **kwargs):
        """
        Keyword arguments:
        names_func -- Returns a list of unique service_names already registered
        on the system.
        """
        self._names_func = names_func
        super(AddServiceForm, self).__init__(*args, **kwargs)

    name = StringField(
        'Service name',
        validators=[
            DataRequired(message='Can’t be empty')
        ]
    )

    def validate_name(self, a):
        from app.utils import email_safe
        # make sure the email_from will be unique to all services
        if email_safe(a.data) in self._names_func():
            raise ValidationError('This service name is already in use')


class ServiceNameForm(Form):
    def __init__(self, names_func, *args, **kwargs):
        """
        Keyword arguments:
        names_func -- Returns a list of unique service_names already registered
        on the system.
        """
        self._names_func = names_func
        super(ServiceNameForm, self).__init__(*args, **kwargs)

    name = StringField(
        u'Service name',
        validators=[
            DataRequired(message='Can’t be empty')
        ])

    def validate_name(self, a):
        from app.utils import email_safe
        # make sure the email_from will be unique to all services
        if email_safe(a.data) in self._names_func():
            raise ValidationError('This service name is already in use')


class ConfirmPasswordForm(Form):
    def __init__(self, validate_password_func, *args, **kwargs):
        self.validate_password_func = validate_password_func
        super(ConfirmPasswordForm, self).__init__(*args, **kwargs)

    password = PasswordField(u'Enter password')

    def validate_password(self, field):
        if not self.validate_password_func(field.data):
            raise ValidationError('Invalid password')


class BaseTemplateForm(Form):
    name = StringField(
        u'Template name',
        validators=[DataRequired(message="Can’t be empty")])

    template_content = TextAreaField(
        u'Message',
        validators=[
            DataRequired(message="Can’t be empty"),
            NoCommasInPlaceHolders()
        ]
    )
    process_type = RadioField(
        'Use priority queue?',
        choices=[
            ('priority', 'Yes'),
            ('normal', 'No'),
        ],
        validators=[DataRequired()],
        default='normal'
    )


class SMSTemplateForm(BaseTemplateForm):
    def validate_template_content(self, field):
        OnlyGSMCharacters()(None, field)


class EmailTemplateForm(BaseTemplateForm):
    subject = TextAreaField(
        u'Subject',
        validators=[DataRequired(message="Can’t be empty")])


class LetterTemplateForm(EmailTemplateForm):

    subject = TextAreaField(
        u'Title',
        validators=[DataRequired(message="Can’t be empty")])

    template_content = TextAreaField(
        u'Body',
        validators=[
            DataRequired(message="Can’t be empty"),
            NoCommasInPlaceHolders()
        ]
    )


class ForgotPasswordForm(Form):
    email_address = email_address(gov_user=False)


class NewPasswordForm(Form):
    new_password = password()


class ChangePasswordForm(Form):
    def __init__(self, validate_password_func, *args, **kwargs):
        self.validate_password_func = validate_password_func
        super(ChangePasswordForm, self).__init__(*args, **kwargs)

    old_password = password('Current password')
    new_password = password('New password')

    def validate_old_password(self, field):
        if not self.validate_password_func(field.data):
            raise ValidationError('Invalid password')


class CsvUploadForm(Form):
    file = FileField('Add recipients', validators=[DataRequired(
        message='Please pick a file'), CsvFileValidator()])


class ChangeNameForm(Form):
    new_name = StringField(u'Your name')


class ChangeEmailForm(Form):
    def __init__(self, validate_email_func, *args, **kwargs):
        self.validate_email_func = validate_email_func
        super(ChangeEmailForm, self).__init__(*args, **kwargs)

    email_address = email_address()

    def validate_email_address(self, field):
        is_valid = self.validate_email_func(field.data)
        if not is_valid:
            raise ValidationError("The email address is already in use")


class ChangeMobileNumberForm(Form):
    mobile_number = mobile_number()


class ConfirmMobileNumberForm(Form):
    def __init__(self, validate_code_func, *args, **kwargs):
        self.validate_code_func = validate_code_func
        super(ConfirmMobileNumberForm, self).__init__(*args, **kwargs)

    sms_code = sms_code()

    def validate_sms_code(self, field):
        is_valid, msg = self.validate_code_func(field.data)
        if not is_valid:
            raise ValidationError(msg)


class ChooseTimeForm(Form):

    def __init__(self, *args, **kwargs):
        super(ChooseTimeForm, self).__init__(*args, **kwargs)
        self.scheduled_for.choices = [('', 'Now')] + [
            get_time_value_and_label(hour) for hour in get_next_hours_until(
                get_furthest_possible_scheduled_time()
            )
        ]
        self.scheduled_for.categories = get_next_days_until(get_furthest_possible_scheduled_time())

    scheduled_for = RadioField(
        'When should Notify send these messages?',
        default='',
        validators=[
            DataRequired()
        ]
    )


class CreateKeyForm(Form):
    def __init__(self, existing_key_names=[], *args, **kwargs):
        self.existing_key_names = [x.lower() for x in existing_key_names]
        super(CreateKeyForm, self).__init__(*args, **kwargs)

    key_type = RadioField(
        'What should Notify do when you use this key?',
        validators=[
            DataRequired()
        ]
    )

    key_name = StringField(u'Description of key', validators=[
        DataRequired(message='You need to give the key a name')
    ])

    def validate_key_name(self, key_name):
        if key_name.data.lower() in self.existing_key_names:
            raise ValidationError('A key with this name already exists')


class SupportType(Form):
    support_type = RadioField(
        'How can we help you?',
        choices=[
            ('problem', 'Report a problem'),
            ('question', 'Ask a question or give feedback'),
        ],
        validators=[DataRequired()]
    )


class Feedback(Form):
    name = StringField('Name')
    email_address = StringField('Email address')
    feedback = TextAreaField('Your message', validators=[DataRequired(message="Can’t be empty")])


class Problem(Feedback):
    email_address = email_address(label='Email address', gov_user=False)


class Triage(Form):
    severe = RadioField(
        'Is it an emergency?',
        choices=[
            ('yes', 'Yes'),
            ('no', 'No'),
        ],
        validators=[DataRequired()]
    )


class RequestToGoLiveForm(Form):
    mou = RadioField(
        (
            'Has your organisation accepted the GOV.UK&nbsp;Notify data sharing and financial '
            'agreement (Memorandum of Understanding)?'
        ),
        choices=[
            ('yes', 'Yes'),
            ('no', 'No'),
            ('don’t know', 'I don’t know')
        ],
        validators=[DataRequired()]
    )
    channel = RadioField(
        'What kind of messages will you be sending?',
        choices=[
            ('emails', 'Emails'),
            ('text messages', 'Text messages'),
            ('emails and text messages', 'Both')
        ],
        validators=[DataRequired()]
    )
    start_date = StringField(
        'When will you be ready to start sending messages?',
        validators=[DataRequired(message='Can’t be empty')]
    )
    start_volume = StringField(
        'How many messages do you expect to send to start with?',
        validators=[DataRequired(message='Can’t be empty')]
    )
    peak_volume = StringField(
        'Will the number of messages increase and when will that start?',
        validators=[DataRequired(message='Can’t be empty')]
    )
    upload_or_api = RadioField(
        'How are you going to send messages?',
        choices=[
            ('File upload', 'Upload a spreadsheet of recipients'),
            ('API', 'Integrate with the GOV.UK Notify API'),
            ('API and file upload', 'Both')
        ],
        validators=[DataRequired()]
    )


class ProviderForm(Form):
    priority = IntegerField('Priority', [validators.NumberRange(min=1, max=100, message="Must be between 1 and 100")])


class ServiceReplyToEmailFrom(Form):
    email_address = email_address(label='Email reply to address')


class ServiceSmsSender(Form):
    sms_sender = StringField(
        'Text message sender',
        validators=[
            Length(max=11, message="Enter 11 characters or fewer")
        ]
    )

    def validate_sms_sender(form, field):
        if field.data and not re.match(r'^[a-zA-Z0-9\s]+$', field.data):
            raise ValidationError('Use letters and numbers only')


class ServiceLetterContactBlock(Form):
    letter_contact_block = TextAreaField()

    def validate_letter_contact_block(form, field):
        line_count = field.data.strip().count('\n')
        if line_count >= 10:
            raise ValidationError(
                'Contains {} lines, maximum is 10'.format(line_count + 1)
            )


class ServiceBrandingOrg(Form):

    def __init__(self, organisations=[], *args, **kwargs):
        self.organisation.choices = organisations
        super(ServiceBrandingOrg, self).__init__(*args, **kwargs)

    branding_type = RadioField(
        'Branding',
        choices=[
            ('govuk', 'GOV.UK only'),
            ('both', 'GOV.UK and organisation'),
            ('org', 'Organisation only')
        ],
        validators=[
            DataRequired()
        ]
    )

    organisation = RadioField(
        'Organisation',
        validators=[
            DataRequired()
        ]
    )


class Whitelist(Form):

    def populate(self, email_addresses, phone_numbers):
        for form_field, existing_whitelist in (
            (self.email_addresses, email_addresses),
            (self.phone_numbers, phone_numbers)
        ):
            for index, value in enumerate(existing_whitelist):
                form_field[index].data = value

    email_addresses = FieldList(
        EmailField(
            '',
            validators=[
                Optional(),
                Email(message='Enter valid email addresses')
            ],
            default=''
        ),
        min_entries=5,
        max_entries=5,
        label="Email addresses"
    )

    phone_numbers = FieldList(
        UKMobileNumber(
            '',
            validators=[
                Optional()
            ],
            default=''
        ),
        min_entries=5,
        max_entries=5,
        label="Mobile numbers"
    )


class DateFilterForm(Form):
    start_date = DateField("Start Date", [validators.optional()])
    end_date = DateField("End Date", [validators.optional()])
    include_from_test_key = BooleanField("Include test keys", default="checked", false_values={"N"})


class ChooseTemplateType(Form):

    template_type = RadioField(
        'What kind of template do you want to add?',
        validators=[
            DataRequired()
        ]
    )

    def __init__(self, include_letters=False, *args, **kwargs):

        super().__init__(*args, **kwargs)

        self.template_type.choices = filter(None, [
            ('email', 'Email'),
            ('sms', 'Text message'),
            ('letter', 'Letter') if include_letters else None
        ])


class SearchTemplatesForm(Form):

    search = SearchField('Search by name')
