"""Interactive first-run configuration wizard for Fedinesia.

Fedinesia - deletes old statuses from fediverse accounts (Mastodon or Pleroma and forks)
Copyright (C) 2021, 2022, 2023  Mark S Burgunder.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import sys
from pathlib import Path

import msgspec
import msgspec.json
from httpx import AsyncClient
from minimal_activitypub import Visibility
from minimal_activitypub.client_2_server import ActivityPub
from minimal_activitypub.client_2_server import ActivityPubError

from fedinesia import CLIENT_WEBSITE
from fedinesia import USER_AGENT
from fedinesia.config import BotConfig
from fedinesia.config import Configuration
from fedinesia.config import MastodonConfig


def collect_bot_config() -> BotConfig:
    """Interactively collect BotConfig values from the user."""
    bot = BotConfig(delete_after=30)
    _get_delete_after(bot)
    _get_skip_bookmarked(bot)
    _get_skip_faved(bot)
    _get_skip_pinned(bot)
    _get_skip_poll(bot)
    _get_skip_dm(bot)
    _get_skip_media(bot)
    _get_skip_faved_at_least(bot)
    _get_skip_boost_at_least(bot)
    _get_skip_reactions_at_least(bot)
    return bot


async def collect_mastodon_config() -> MastodonConfig:
    """Interactively collect MastodonConfig values from the user."""
    instance = input("[..] Enter instance (domain name) for Mastodon account host: ")

    try:
        async with AsyncClient(http2=True) as client:
            client_id, client_secret = await ActivityPub.create_app(
                instance_url=instance,
                client=client,
                user_agent=USER_AGENT,
                client_website=CLIENT_WEBSITE,
            )

            authorization_request_url = await ActivityPub.generate_authorization_url(
                instance_url=instance,
                client_id=client_id,
                user_agent=USER_AGENT,
            )
            print(f"Please go to the following URL and follow the instructions:\n{authorization_request_url}")
            authorization_code = input("[...] Please enter the authorization code:")

            access_token = await ActivityPub.validate_authorization_code(
                client=client,
                instance_url=instance,
                authorization_code=authorization_code,
                client_id=client_id,
                client_secret=client_secret,
            )

    except ActivityPubError as error:
        print(f"! Error when setting up Fediverse connection: {error}")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)

    return MastodonConfig(instance=instance, access_token=access_token)


async def setup_shop(
    config_file: str,
) -> Configuration:
    """Process command line arguments, establish debug logging to file if
    specified, load configuration.

    :returns:
        Configuration: config for this run of the Fedinesia.
    """
    config_file_path = Path(config_file)
    if config_file_path.exists():
        with config_file_path.open(mode="rb") as config_file_file:
            config: Configuration = msgspec.json.decode(config_file_file.read(), type=Configuration)

    else:
        bot = collect_bot_config()
        mastodon = await collect_mastodon_config()
        config = Configuration(bot=bot, mastodon=mastodon)

        config_encoded = msgspec.json.encode(config)
        config_json = msgspec.json.format(config_encoded, indent=4)
        with config_file_path.open(mode="wb") as config_file_file:
            config_file_file.write(config_json)

    return config


def _get_delete_after(bot: BotConfig) -> None:
    print('Please enter maximum age of retained statuses in the format of "number unit"')
    print('For example "1 weeks" or "3 days". Supported units are:')
    print(" - seconds\n - minutes\n - hours\n - days\n - weeks\n - months")
    max_age = input("[..] Minimum age to delete statuses (in seconds): ")
    max_age_parts = max_age.split(" ")
    max_age_number = int(max_age_parts[0])
    max_age_unit = max_age_parts[1]
    if max_age_unit == "seconds":
        bot.delete_after = max_age_number
    elif max_age_unit == "minutes":
        bot.delete_after = max_age_number * 60
    elif max_age_unit == "hours":
        bot.delete_after = max_age_number * 3600
    elif max_age_unit == "days":
        bot.delete_after = max_age_number * 3600 * 24
    elif max_age_unit == "weeks":
        bot.delete_after = max_age_number * 3600 * 24 * 7
    elif max_age_unit == "months":
        bot.delete_after = max_age_number * 3600 * 24 * 30
    else:
        print("! Error ... unknown unit ({max_age_unit}) specified")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_poll(bot: BotConfig) -> None:
    print("Should polls be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_poll = False
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_poll = True
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_dm(bot: BotConfig) -> None:
    print("Should Direct Messages be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_visibility = []
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_visibility = [Visibility.DIRECT]
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_media(bot: BotConfig) -> None:
    print("Should Statuses with attachments / pictures be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_media = False
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_media = True
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_faved_at_least(bot: BotConfig) -> None:
    print(
        "Should statuses being favourited a certain minimum number of times be "
        "excluded from deletion even when they get old enough?"
    )
    print("(enter 0 to disregard this setting)")
    bot.skip_deleting_faved_at_least = int(input("[..] Please enter number: "))


def _get_skip_boost_at_least(bot: BotConfig) -> None:
    print(
        "Should statuses being boosted a certain minimum number of times be "
        "excluded from deletion even when they get old enough?"
    )
    print("(enter 0 to disregard this setting)")
    bot.skip_deleting_boost_at_least = int(input("[..] Please enter number: "))


def _get_skip_reactions_at_least(bot: BotConfig) -> None:
    print(
        "Should statuses with a certain minimum number of reactions be "
        "excluded from deletion even when they get old enough?"
    )
    print("(enter 0 to disregard this setting)")
    bot.skip_deleting_reactions_at_least = int(input("[..] Please enter number: "))


def _get_skip_pinned(bot: BotConfig) -> None:
    print("Should pinned statuses be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_pinned = False
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_pinned = True
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_faved(bot: BotConfig) -> None:
    print("Should favoured statuses be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_faved = False
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_faved = True
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)


def _get_skip_bookmarked(bot: BotConfig) -> None:
    print("Should bookmarked statuses be deleted when they get old enough?")
    y_or_n = input("[..] Please enter Y for yes or N for no: ")
    if y_or_n in ("Y", "y"):
        bot.skip_deleting_bookmarked = False
    elif y_or_n in ("N", "n"):
        bot.skip_deleting_bookmarked = True
    else:
        print("! ERROR ... please only respond with 'Y' or 'N'")
        print("! Cannot continue. Exiting now.")
        sys.exit(1)
