import asyncio
import copy
import json
import logging
import os
from json.decoder import JSONDecodeError
from urllib.parse import urlparse
import re

import aiohttp
import discord
from ark_discord_bot.models import RatesStatus
from discord.ext import commands

logger = logging.getLogger(__name__)

# create cog class
class Rates(commands.Cog):
    DEFAULT_SERVER_INFO = {
        "smalltribes": {
            "short_name": "Smalltribes",
            "color": 0xA34C44,
        },
        "arkpocalypse": {
            "short_name": "Arkpocalypse",
            "color": 0x562B61,
        },
        "conquest": {
            "short_name": "Conquest/Classic",
            "color": 0x6DFF90,
        },
        None: {
            "short_name": "Official",
            "color": 0x63BCC3,
        },
    }

    def __init__(self, client):
        self.client = client

        self.webpage_urls = client.config.rates_urls
        self.channel_id = client.config.rates_channel_id
        self.polling_delay = client.config.polling_delay
        self.allowed_roles = client.config.allowed_roles
        self.data_dir = os.path.expanduser(client.config.data_dir)
        self.output_paths = [
            os.path.join(
                self.data_dir,
                f"last_{os.path.splitext(os.path.basename(urlparse(url).path))[0]}.json",
            )
            for url in client.config.rates_urls
        ]
        # Create parent directory for persistent data if it doesn't exist yet
        if not os.path.exists(self.data_dir):
            os.makedirs(self.data_dir)

    @staticmethod
    async def get_current_rates(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(
                url,
                headers={"Pragma": "no-cache", "Cache-Control": "no-cache"},
            ) as response:
                response = await response.text()
                rates = RatesStatus.from_raw(response)
                return rates

    async def send_embed(self, description, url):
        # generate embed

        try:
            server_match_dict = (
                re.match(
                    "(?P<host>.*\/)?(?:(?P<platform>.*)\_(?P<game_mode>.*)\_)?dynamicconfig\.ini",
                    os.path.basename(urlparse(url).path),
                )
            ).groupdict()
            server_meta = self.DEFAULT_SERVER_INFO.get(
                server_match_dict.get("game_mode")
            )
            # TODO: Add ability to accept custom rates URL

        except Exception as e:
            logger.error(f"Rates url could not be processed: {url} {e}")
            pass

        else:
            server_name = server_meta.get("short_name")

            embed = discord.Embed(
                description=description,
                title=f"ARK's {server_name} server rates have just been updated!",
                color=server_meta.get("color"),
            )

            # send embed
            channel = self.client.get_channel(self.channel_id)
            message = await channel.send(embed=embed)

            # if in announcement channel, publish message
            if message.channel.type == discord.ChannelType.news:
                logger.info("Announcement channel detected: Publishing message")
                await message.publish()

    # Events
    @commands.Cog.listener()
    async def on_ready(self):
        logger.info("Cog Ready: Rates")

        while True:
            for url, output_path in zip(self.webpage_urls, self.output_paths):
                # get current rates from ARK Web API
                logger.info(f"Retrieving current rates at {url}")
                try:
                    rates = await self.get_current_rates(url)

                except ValueError:
                    logger.error(f"Could not process rates URL {url}")
                except Exception as e:
                    logger.error(
                        f"Could not retrieve rates from ARK Web API at {url}: {e}"
                    )
                    await asyncio.sleep(self.polling_delay)
                    continue

                # get old rates from file
                try:
                    with open(output_path) as f:
                        last_rates_dict = json.load(f)
                except (FileNotFoundError, JSONDecodeError) as e:
                    logger.warn(f"Problem loading file at {output_path}: {e}")
                    last_rates_dict = copy.deepcopy(rates).to_dict()
                    try:
                        with open(output_path, "w+") as f:
                            json.dump(rates.to_dict(), f, indent=4)
                    except Exception as e:
                        logger.error(e)
                        await asyncio.sleep(self.polling_delay)
                        continue

                last_rates = RatesStatus.from_dict(last_rates_dict)

                # compare rates to last rates
                rates_diff = rates.get_diff(last_rates)

                if rates_diff.items:

                    # save rates to file
                    try:
                        with open(output_path, "w+") as f:
                            json.dump(rates.to_dict(), f, indent=4)
                    except Exception as e:
                        logger.error(e)
                        await asyncio.sleep(self.polling_delay)
                        continue

                    # generate and send embed
                    logger.info(f"Rates at {url} changed - sending embed")
                    embed_description = rates_diff.to_embed(rates)
                    await self.send_embed(embed_description, url)

            await asyncio.sleep(self.polling_delay)


# add cog to client
def setup(client):
    client.add_cog(Rates(client))
