"""
RecruitMe Cog
"""

# Standard Library
import logging
from enum import Enum
from typing import Any

# Third Party
from discord import ButtonStyle, ChannelType, Embed, Guild, Interaction, Member, ui
from discord.ext import commands

# Django
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone

# Alliance Auth
from allianceauth.eveonline.models import EveCharacter

# Alliance Auth Discord Bot
from aadiscordbot import app_settings
from aadiscordbot.utils import auth

# Terra Nanotech Discordbot Cogs
from tnnt_discordbot_cogs.helper import reverse_absolute, unload_cog
from tnnt_discordbot_cogs.models.setting import Setting

logger = logging.getLogger(__name__)

# Discord Bot Settings
# Audit System
AUDIT_SYSTEM_NAME = "Character Audit"
AUDIT_SYSTEM_URL = reverse_absolute(viewname="corptools:react")
AUDIT_SYSTEM_URL_MD = f"[{AUDIT_SYSTEM_NAME}]({AUDIT_SYSTEM_URL})"

# Groups Page URL
GROUPS_PAGE_URL = reverse_absolute(viewname="groupmanagement:groups")

# Roles
APPLICANT_ROLE_NAME = Setting.get_setting(Setting.Field.APPLICANT_ROLE_NAME.value)
APPLICANT_ROLE_ID = Setting.get_setting(Setting.Field.APPLICANT_ROLE_ID.value)
RECRUITER_ROLE_ID = Setting.get_setting(Setting.Field.RECRUITER_ROLE_ID.value)
LEADERSHIP_ROLE_ID = Setting.get_setting(Setting.Field.LEADERSHIP_ROLE_ID.value)

# Channels
RECRUITING_CHANNEL = Setting.get_setting(Setting.Field.RECRUITING_CHANNEL.value).channel


class BotResponse(str, Enum):
    """
    Bot responses for the RecruitMe cog
    """

    # Not in the application group
    NOT_IN_APPLICATION_GROUP = (
        f"**You are not in the `{APPLICANT_ROLE_NAME}` group.**\n\n"
        f"Please open the [groups page]({GROUPS_PAGE_URL}) and "
        f"join the `{APPLICANT_ROLE_NAME}` group first …"
    )

    # Member not in the application group
    MEMBER_NOT_IN_APPLICATION_GROUP = (
        "**{MEMBER} is not in the "
        f"`{APPLICANT_ROLE_NAME}` group.**\n\n"
        "Please let them know to open the "
        f"[groups page]({GROUPS_PAGE_URL}) and join the "
        f"`{APPLICANT_ROLE_NAME}` group first …"
    )

    # Not a recruiter
    NOT_A_RECRUITER = (
        "You are not a recruiter for Terra Nanotech and "
        "cannot use this command on this user …"
    )

    # Private thread guide title and body
    PRIVATE_THREAD_GUIDE_TITLE = "Private Thread Guide"
    PRIVATE_THREAD_GUIDE_BODY = (
        "To add a person to this thread simply `@ping` them. "
        "This works with `@groups` as well to bulk add people to the channel. "
        "Use wisely, abuse will not be tolerated."
    )

    # Recruitment thread title and body
    RECRUITMENT_THREAD_TITLE = "{MAIN_CHARACTER} | Recruitment | {DATE}"
    RECRUITMENT_THREAD_WELCOME = (
        "Hello <@{MEMBER_ID}>, and welcome! :wave:\n\n"
        "We're excited that you're interested in joining Terra Nanotech!\n\n"
        f"Before we get started, please ensure **all** your characters are added to {AUDIT_SYSTEM_URL_MD}.\n"
        "This includes your main character as well as any other characters you have.\n\n"
        'Once done, please click the "**Continue**" button below to notify a recruiter.\n'
        "If you are not certain yet and just want to ask some questions first to help "
        'you to decide, click the "**Omit This Step**" button.'
    )
    RECRUITMENT_THREAD_COMPLIANCE_REMINDER = (
        "Before we can proceed with your recruitment, "
        f"please ensure **all** your characters are added to {AUDIT_SYSTEM_URL_MD}.\n"
        "This includes your main character as well as any other characters you have.\n\n"
        # "The following characters are not compliant:\n"
        # "{CHARACTERS_LIST}\n\n"
        'Once done, please click the "**Continue**" button below to notify a recruiter.\n\n'
        "Thank you for your understanding!"
    )
    RECRUITMENT_THREAD_RECRUITING_START = (
        "Thank you for reaching out!\n\n"
        f"A member of the <@&{RECRUITER_ROLE_ID}> will be with you shortly to "
        "guide you through the next steps of the recruitment process, which "
        "may include an interview for further assessments.\n\n"
        "You may start the conversation by introducing yourself and tell us a little bit about you.\n"
        "- Who are you?\n"
        "- What are your experiences in EVE Online so far? (Tell us a bit about your journey/history in EVE)\n"
        "- Why would we consider you?\n"
        "- What makes you interesting?\n"
        "- What would you bring to the table?\n"
        "- In which way the corp would benefit from you?\n"
        "- What do you expect?\n\n"
        "And of cource, if you have any questions, feel free to ask!\n\n"
        "Thank you for your patience!\n\n"
        f"(Adding <@&{LEADERSHIP_ROLE_ID}> to the thread for visibility.)"
    )
    RECRUITMENT_THREAD_QUESTIONS_ONLY_START = (
        "Thank you for reaching out!\n\n"
        f"A member of the <@&{RECRUITER_ROLE_ID}> will be with you shortly "
        "to answer any questions you may have.\n\n"
        "Thank you for your patience!\n\n"
        f"(Adding <@&{LEADERSHIP_ROLE_ID}> to the thread for visibility.)"
    )
    RECRUITMENT_THREAD_CLOSURE = (
        "Your recruitment thread has been closed.\n\n"
        "If you change your mind, feel free to use the `/recruit_me` command again!"
    )

    # Recruitment thread created message
    RECRUITMENT_THREAD_CREATED = "Recruitment thread created!"


def _get_auth_user_and_main_character(
    discord_user_id: int, guild: Guild
) -> tuple[User, EveCharacter]:
    """
    Get the auth user and main character for a member

    :param discord_user_id:
    :type discord_user_id:
    :param guild:
    :type guild:
    :return:
    :rtype:
    """

    auth_user = auth.get_auth_user(user=discord_user_id, guild=guild)
    main_character = auth_user.profile.main_character

    return auth_user, main_character


def _check_compliance(auth_user: User) -> tuple[bool, Any]:
    """
    Check if all characters of the auth user are in the audit system

    :param auth_user:
    :type auth_user:
    :return:
    :rtype:
    """

    character_ownerships = auth_user.character_ownerships.all().select_related(
        "character", "character__characteraudit"
    )
    characters = []

    for character in character_ownerships:
        # if character.user.id not in characters:
        #     characters[character.user.id] = []

        try:
            # if not character.character.characteraudit.is_active():
            if not character.character.characteraudit:
                # characters[character.user.id].append(character.character.character_name)
                characters.append(character.character.character_name)
        except ObjectDoesNotExist:
            characters.append(character.character.character_name)

    return True if len(characters) == 0 else False, characters


class ComplianceView(ui.View):
    """
    View for compliance check
    """

    def __init__(self):
        """
        Initialize the ComplianceView
        """

        super().__init__(timeout=None)  # No timeout

        self.embed = Embed(
            title=BotResponse.PRIVATE_THREAD_GUIDE_TITLE.value,
            description=BotResponse.PRIVATE_THREAD_GUIDE_BODY.value,
        )

    @ui.button(
        label="Continue",
        row=0,
        style=ButtonStyle.success,
        custom_id="tnnt_cogs:recruitment:compliance_view:button:continue",
    )
    async def continue_button_callback(self, button, interaction):
        """
        Continue button callback

        :param button:
        :type button:
        :param interaction:
        :type interaction:
        :return:
        :rtype:
        """

        self.disable_all_items()

        await interaction.response.edit_message(view=self)

        auth_user, main_character = _get_auth_user_and_main_character(
            discord_user_id=interaction.user.id, guild=interaction.guild
        )

        is_compliant, non_compliant_characters = _check_compliance(auth_user)

        if not is_compliant:
            characters_list = "\n".join(
                f"- {char}" for char in non_compliant_characters
            )

            await interaction.channel.send(
                content=BotResponse.RECRUITMENT_THREAD_COMPLIANCE_REMINDER.value.format(
                    CHARACTERS_LIST=characters_list
                ),
                view=ComplianceView(),
            )
        else:
            await interaction.channel.send(
                content=BotResponse.RECRUITMENT_THREAD_RECRUITING_START.value.format(
                    LEADERSHIP_ROLE_ID=LEADERSHIP_ROLE_ID,
                    RECRUITER_ROLE_ID=RECRUITER_ROLE_ID,
                ),
                embed=self.embed,
            )

    @ui.button(
        label="Close Thread",
        row=0,
        style=ButtonStyle.danger,
        custom_id="tnnt_cogs:recruitment:compliance_view:button:close_thread",
    )
    async def close_thread_button_callback(self, button, interaction):
        """
        Close thread button callback

        :param button:
        :type button:
        :param interaction:
        :type interaction:
        :return:
        :rtype:
        """

        self.disable_all_items()

        await interaction.response.edit_message(view=self)
        await interaction.channel.send(
            content=BotResponse.RECRUITMENT_THREAD_CLOSURE.value
        )

        return await interaction.channel.archive()

    # @ui.button(
    #     label="Identify Me!",
    #     row=0,
    #     style=ButtonStyle.danger,
    #     custom_id="tnnt_cogs:recruitment:compliance_view:button:identify_me",
    # )
    # async def identify_me_button_callback(self, button, interaction):
    #     """
    #     Identify me button callback
    #
    #     :param button:
    #     :type button:
    #     :param interaction:
    #     :type interaction:
    #     :return:
    #     :rtype:
    #     """
    #
    #     await interaction.response.edit_message(view=self)
    #     await interaction.channel.send(
    #         content=f"Hello <@{interaction.user.id}>! You have identified yourself in this recruitment thread."
    #     )
    #     await interaction.channel.send(
    #         content=('Channel Title: "' + interaction.channel.name + '"')
    #     )
    #
    #     auth_user, main_character = _get_auth_user_and_main_character(
    #         discord_user_id=interaction.user.id, guild=interaction.guild
    #     )
    #
    #     await interaction.channel.send(
    #         content=('Your main character is: "' + main_character.character_name + '"')
    #     )


class RecruitmentThreadIntroduction(ui.View):
    """
    View for the recruitment thread introduction
    """

    # def __init__(self, auth_user: User):
    def __init__(self):
        """
        Initialize the RecruitmentThreadIntroduction view
        """

        super().__init__(timeout=None)

        self.embed = Embed(
            title=BotResponse.PRIVATE_THREAD_GUIDE_TITLE.value,
            description=BotResponse.PRIVATE_THREAD_GUIDE_BODY.value,
        )

    @ui.button(
        label="Continue",
        row=0,
        style=ButtonStyle.success,
        custom_id="tnnt_cogs:recruitment:recruitment_threat_introduction_view:button:continue",
    )
    async def continue_button_callback(self, button, interaction):
        """
        Continue button callback

        :param button:
        :type button:
        :param interaction:
        :type interaction:
        :return:
        :rtype:
        """

        self.disable_all_items()

        await interaction.response.edit_message(view=self)

        auth_user, main_character = _get_auth_user_and_main_character(
            discord_user_id=interaction.user.id, guild=interaction.guild
        )

        is_compliant, non_compliant_characters = _check_compliance(auth_user)

        if not is_compliant:
            characters_list = "\n".join(
                f"- {char}" for char in non_compliant_characters
            )

            await interaction.channel.send(
                content=BotResponse.RECRUITMENT_THREAD_COMPLIANCE_REMINDER.value.format(
                    CHARACTERS_LIST=characters_list
                ),
                view=ComplianceView(),
            )
        else:
            await interaction.channel.send(
                content=BotResponse.RECRUITMENT_THREAD_RECRUITING_START.value.format(
                    LEADERSHIP_ROLE_ID=LEADERSHIP_ROLE_ID,
                    RECRUITER_ROLE_ID=RECRUITER_ROLE_ID,
                ),
                embed=self.embed,
            )

    @ui.button(
        label="Omit This Step",
        row=0,
        style=ButtonStyle.secondary,
        custom_id="tnnt_cogs:recruitment:recruitment_threat_introduction_view:button:omit_this_step",
    )
    async def omit_button_callback(self, button, interaction):
        """
        Omit this step button callback

        :param button:
        :type button:
        :param interaction:
        :type interaction:
        :return:
        :rtype:
        """

        self.disable_all_items()

        await interaction.response.edit_message(view=self)
        await interaction.channel.send(
            content=BotResponse.RECRUITMENT_THREAD_QUESTIONS_ONLY_START.value.format(
                LEADERSHIP_ROLE_ID=LEADERSHIP_ROLE_ID,
                RECRUITER_ROLE_ID=RECRUITER_ROLE_ID,
            ),
            embed=self.embed,
        )

    @ui.button(
        label="Close Thread",
        row=0,
        style=ButtonStyle.danger,
        custom_id="tnnt_cogs:recruitment:recruitment_threat_introduction_view:button:close_thread",
    )
    async def close_thread_button_callback(self, button, interaction):
        """
        Close thread button callback

        :param button:
        :type button:
        :param interaction:
        :type interaction:
        :return:
        :rtype:
        """

        self.disable_all_items()

        await interaction.response.edit_message(view=self)
        await interaction.channel.send(
            content=BotResponse.RECRUITMENT_THREAD_CLOSURE.value
        )

        return await interaction.channel.archive()

    # @ui.button(
    #     label="Identify Me!",
    #     row=0,
    #     style=ButtonStyle.danger,
    #     custom_id="tnnt_cogs:recruitment:recruitment_threat_introduction_view:button:identify_me",
    # )
    # async def identify_me_button_callback(self, button, interaction):
    #     """
    #     Identify me button callback
    #
    #     :param button:
    #     :type button:
    #     :param interaction:
    #     :type interaction:
    #     :return:
    #     :rtype:
    #     """
    #
    #     await interaction.response.edit_message(view=self)
    #     await interaction.channel.send(
    #         content=f"Hello <@{interaction.user.id}>! You have identified yourself in this recruitment thread."
    #     )
    #     await interaction.channel.send(
    #         content=('Channel Title: "' + interaction.channel.name + '"')
    #     )
    #
    #     auth_user, main_character = _get_auth_user_and_main_character(
    #         discord_user_id=interaction.user.id, guild=interaction.guild
    #     )
    #
    #     await interaction.channel.send(
    #         content=('Your main character is: "' + main_character.character_name + '"')
    #     )


class RecruitMe(commands.Cog):
    """
    Thread Tools for recruiting!
    """

    def __init__(self, bot):
        """
        Initialize the RecruitMe cog

        :param bot: The bot instance
        :type bot: discord.ext.commands.Bot
        """

        logger.info(msg="RecruitMe cog initialized")

        self.bot = bot
        self._on_ready_done = False
        self.recruiting_channel = (
            None  # Optional: Resolved channel reference available after ready
        )

    @commands.Cog.listener()
    async def on_ready(self):
        # `on_ready` can fire multiple times; run initialization once
        if self._on_ready_done:
            return

        # Ensure bot is ready (usually redundant inside on_ready)
        if not self.bot.is_ready():
            await self.bot.wait_until_ready()

        logger.info("RecruitMe cog received on_ready")

        # Resolve the recruiting channel id into a Channel object for later use
        try:
            self.recruiting_channel = self.bot.get_channel(RECRUITING_CHANNEL)

            logger.info("Recruiting channel resolved: %s", self.recruiting_channel)
        except Exception:
            self.recruiting_channel = None

            logger.exception("Failed to resolve recruiting channel")

        # Register RecruitmentThreadIntroduction view
        try:
            self.bot.add_view(RecruitmentThreadIntroduction())

            logger.info("RecruitmentThreadIntroduction view added successfully")
        except Exception:
            logger.exception("Failed to add RecruitmentThreadIntroduction view")

        # Register ComplianceView view
        try:
            self.bot.add_view(ComplianceView())

            logger.info("ComplianceView view added successfully")
        except Exception:
            logger.exception("Failed to add ComplianceView view")

        # Mark initialization done so this runs only once
        self._on_ready_done = True

        logger.info("RecruitMe cog on_ready processing complete")

    @staticmethod
    async def open_ticket(ctx: Interaction, member: Member):
        """
        Open a recruitment ticket

        :param ctx: The context of the command
        :type ctx: discord.Interaction
        :param member: The member requesting a recruiter
        :type member: discord.Member
        :return: None
        :rtype: None
        """

        # Get the auth user and main character
        auth_user, main_character = _get_auth_user_and_main_character(
            discord_user_id=member.id, guild=ctx.guild
        )

        # Get the recruiting channel
        ch = ctx.guild.get_channel(RECRUITING_CHANNEL)

        # Create the recruitment thread
        th = await ch.create_thread(
            name=BotResponse.RECRUITMENT_THREAD_TITLE.value.format(
                MAIN_CHARACTER=main_character,
                DATE=timezone.now().strftime("%Y-%m-%d %H:%M"),
            ),
            auto_archive_duration=10080,
            type=ChannelType.private_thread,
            reason=None,
        )

        # Prepare the welcome message
        msg = BotResponse.RECRUITMENT_THREAD_WELCOME.value.format(
            LEADERSHIP_ROLE_ID=LEADERSHIP_ROLE_ID,
            RECRUITER_ROLE_ID=RECRUITER_ROLE_ID,
            MEMBER_ID=member.id,
        )

        # Send the welcome message in the recruitment thread
        await th.send(content=msg, view=RecruitmentThreadIntroduction())

        # Notify the user that their recruitment thread has been created
        await ctx.response.send_message(
            content=BotResponse.RECRUITMENT_THREAD_CREATED.value,
            view=None,
            ephemeral=True,
        )

    @commands.slash_command(
        name="recruit_me",
        description="Get hold of a recruiter",
        guild_ids=app_settings.get_all_servers(),
    )
    async def slash_recruit_me(self, ctx):
        """
        Get hold of a recruiter

        :param ctx: The context of the command
        :type ctx: discord.Interaction
        :return: None
        :rtype: None
        """

        if APPLICANT_ROLE_ID not in [role.id for role in ctx.user.roles]:
            return await ctx.respond(
                BotResponse.NOT_IN_APPLICATION_GROUP.value, ephemeral=True
            )

        await self.open_ticket(ctx=ctx, member=ctx.user)

        return None

    @commands.message_command(
        name="Create Recruitment Thread", guild_ids=app_settings.get_all_servers()
    )
    async def reverse_recruit_msg_context(self, ctx, message):
        """
        Help a new guy get a recruiter

        :param ctx: The context of the command
        :type ctx: discord.Interaction
        :param message: The message that triggered the command
        :type message: discord.Message
        :return: None
        :rtype: None
        """

        # Check if the user is a recruiter and if the user is different from the target user
        if RECRUITER_ROLE_ID not in [role.id for role in ctx.user.roles] and int(
            ctx.user.id
        ) != int(message.author.id):
            return await ctx.respond(BotResponse.NOT_A_RECRUITER.value, ephemeral=True)

        # Check if the target user is in the Applicants group
        if APPLICANT_ROLE_ID not in [role.id for role in message.author.roles]:
            return await ctx.respond(
                BotResponse.MEMBER_NOT_IN_APPLICATION_GROUP.value.format(
                    MEMBER=message.author.mention,
                ),
                ephemeral=True,
            )

        await self.open_ticket(ctx=ctx, member=message.author)

        return None

    @commands.user_command(
        name="Recruit Member", guild_ids=app_settings.get_all_servers()
    )
    async def reverse_recruit_user_context(self, ctx, user):
        """
        Help a new guy get a recruiter

        :param ctx: The context of the command
        :type ctx: discord.Interaction
        :param user: The user that triggered the command
        :type user: discord.User
        :return: None
        :rtype: None
        """

        # Check if the user is a recruiter and if the user is different from the target user
        if RECRUITER_ROLE_ID not in [role.id for role in ctx.user.roles] and int(
            ctx.user.id
        ) != int(user.id):
            return await ctx.respond(BotResponse.NOT_A_RECRUITER.value, ephemeral=True)

        # Check if the target user is in the Applicants group
        if APPLICANT_ROLE_ID not in [role.id for role in user.roles]:
            return await ctx.respond(
                BotResponse.MEMBER_NOT_IN_APPLICATION_GROUP.value.format(
                    MEMBER=user.mention
                ),
                ephemeral=True,
            )

        await self.open_ticket(ctx=ctx, member=user)

        return None


def setup(bot):
    """
    Set up the RecruitMe cog

    :param bot:
    :type bot:
    :return:
    :rtype:
    """

    # Unload the RecruitMe cog from `aadiscordbot`, so we can load our own.
    unload_cog(bot=bot, cog_name="RecruitMe")

    # Add the RecruitMe cog to the bot
    bot.add_cog(RecruitMe(bot))
