from typing import List

from maps_adv.adv_store.lib.db import db
from maps_adv.adv_store.v2.lib.db import tables

from .get_campaign import get_campaign


async def _create_billing(data: dict) -> int:
    """ create billing and billing method(fix, cpm, cpa)

    :param data: dict from 'billing.proto::Billing'
    :return: id for newly created billing
    """

    billing_data = {"fix_id": None, "cpm_id": None, "cpa_id": None}

    if data.get("fix"):
        billing_data["fix_id"] = await db.rw.fetch_val(
            tables.fix.insert()
            .values(
                {
                    "cost": data["fix"]["cost"],
                    "time_interval": data["fix"]["time_interval"],
                }
            )
            .returning(tables.fix.c.id)
        )
    elif data.get("cpm"):
        billing_data["cpm_id"] = await db.rw.fetch_val(
            tables.cpm.insert()
            .values(
                {
                    "cost": data["cpm"]["cost"],
                    "budget": data["cpm"].get("budget"),
                    "daily_budget": data["cpm"].get("daily_budget"),
                }
            )
            .returning(tables.cpm.c.id)
        )
    elif data.get("cpa"):
        billing_data["cpa_id"] = await db.rw.fetch_val(
            tables.cpa.insert()
            .values(
                {
                    "cost": data["cpa"]["cost"],
                    "budget": data["cpa"].get("budget"),
                    "daily_budget": data["cpa"].get("daily_budget"),
                }
            )
            .returning(tables.cpa.c.id)
        )
    return await db.rw.fetch_val(
        tables.billing.insert().values(billing_data).returning(tables.billing.c.id)
    )


async def _create_campaign(data: dict, billing_id) -> int:
    """create campaign in the Campaign table

    :param data: dict from 'campaign.proto::CampaignData'
    :return: id for newly created campaign
    """
    values = {
        "author_id": data["author_id"],
        "name": data["name"],
        "publication_envs": data["publication_envs"],
        "campaign_type": data["campaign_type"],
        "start_datetime": data["start_datetime"],
        "end_datetime": data["end_datetime"],
        "timezone": data["timezone"],
        "billing_id": billing_id,
        "platforms": data["platforms"],
        "order_id": data["order_id"],
        "manul_order_id": data.get("manul_order_id"),
        "comment": data["comment"],
        "user_display_limit": data["user_display_limit"],
        "user_daily_display_limit": data["user_daily_display_limit"],
        "targeting": data["targeting"],
        "rubric": data["rubric"],
        "display_probability": data.get("display_probability"),
        "display_probability_auto": data.get("display_probability_auto"),
    }
    return await db.rw.fetch_val(
        tables.campaign.insert().values(values).returning(tables.campaign.c.id)
    )


async def _create_week_schedule(data: List[dict], campaign_id) -> None:
    """create many week_schedule-s in bulk

    :param data: list of 'campaign.proto::WeekScheduleItem' dicts
    """
    await db.rw.execute_many(
        tables.week_schedule.insert(),
        [
            {"campaign_id": campaign_id, "start": sch["start"], "end": sch["end"]}
            for sch in data
        ],
    )


async def _create_creatives(creatives: List[dict], campaign_id) -> None:
    """create several creatives of different types

    :param creatives: list of c'reative.proto::Creative' dicts
    """
    queries = []

    for creative in creatives:
        if "pin" in creative:
            pin = {
                "campaign_id": campaign_id,
                "images": creative["pin"]["images"],
                "title": creative["pin"]["title"],
                "subtitle": creative["pin"]["subtitle"],
            }
            queries.append(tables.pin.insert().values(pin))
        elif "billboard" in creative:
            billboard = {
                "campaign_id": campaign_id,
                "images": creative["billboard"]["images"],
            }
            queries.append(tables.billboard.insert().values(billboard))
        elif "logo_and_text" in creative:
            logo_and_text = {
                "campaign_id": campaign_id,
                "images": creative["logo_and_text"]["images"],
                "text": creative["logo_and_text"]["text"],
            }
            queries.append(tables.logo_and_text.insert().values(logo_and_text))
        elif "banner" in creative:
            banner = {
                "campaign_id": campaign_id,
                "images": creative["banner"]["images"],
                "disclaimer": creative["banner"]["disclaimer"],
                "show_ads_label": creative["banner"]["show_ads_label"],
                "title": creative["banner"]["title"],
                "description": creative["banner"]["description"],
                "terms": creative["banner"]["terms"],
            }
            queries.append(tables.banner.insert().values(banner))
        elif "icon" in creative:
            icon = {
                "campaign_id": campaign_id,
                "images": creative["icon"]["images"],
                "title": creative["icon"]["title"],
                "position": creative["icon"]["position"],
            }
            queries.append(tables.icon.insert().values(icon))
        elif "pin_search" in creative:
            pin_search = {
                "campaign_id": campaign_id,
                "images": creative["pin_search"]["images"],
                "title": creative["pin_search"]["title"],
                "organizations": creative["pin_search"]["organizations"],
            }
            queries.append(tables.pin_search.insert().values(pin_search))
        elif "text" in creative:
            text = {
                "campaign_id": campaign_id,
                "text": creative["text"]["text"],
                "disclaimer": creative["text"]["disclaimer"],
            }
            queries.append(tables.text.insert().values(text))
        elif "via_point" in creative:
            text = {
                "campaign_id": campaign_id,
                "images": creative["via_point"]["images"],
                "button_text_active": creative["via_point"]["button_text_active"],
                "button_text_inactive": creative["via_point"]["button_text_inactive"],
                "description": creative["via_point"]["description"],
            }
            queries.append(tables.via_point.insert().values(text))
        else:
            raise ValueError("Data Managers (creating campaign): invalid creative")

    for query in queries:
        await db.rw.execute(query)


async def _create_actions(actions: List[dict], campaign_id) -> None:
    """create several actions of different types

    :param actions: list of dicts from 'action.proto::Action'
    """
    queries = []
    for action in actions:
        if "open_site" in action:
            open_site = {
                "campaign_id": campaign_id,
                "title": action["open_site"]["title"],
                "url": action["open_site"]["url"],
            }
            queries.append(tables.open_site.insert().values(open_site))
        elif "search" in action:
            search = {
                "campaign_id": campaign_id,
                "title": action["search"]["title"],
                "organizations": action["search"]["organizations"],
                "history_text": action["search"]["history_text"],
            }
            queries.append(tables.search.insert().values(search))
        elif "phone_call" in action:
            phone_call = {
                "campaign_id": campaign_id,
                "title": action["phone_call"]["title"],
                "phone": action["phone_call"]["phone"],
            }
            queries.append(tables.phone_call.insert().values(phone_call))
        elif "download_app" in action:
            download_app = {
                "campaign_id": campaign_id,
                "title": action["download_app"]["title"],
                "google_play_id": action["download_app"]["google_play_id"],
                "app_store_id": action["download_app"]["app_store_id"],
                "url": action["download_app"]["url"],
            }
            queries.append(tables.download_app.insert().values(download_app))
        else:
            raise ValueError("Data Managers (creating campaign): invalid action")

    for query in queries:
        await db.rw.execute(query)


async def _create_placing(data: dict, campaign_id: int) -> None:
    """create one placing

    :param data: dict from 'placing.proto::Placing'
    """
    if "organizations" in data:
        await db.rw.execute(
            tables.organizations.insert().values(
                {
                    "campaign_id": campaign_id,
                    "permalinks": data["organizations"]["permalinks"],
                }
            )
        )
    elif "area" in data:

        await db.rw.execute(
            tables.area.insert().values(
                {
                    "campaign_id": campaign_id,
                    "areas": data["area"]["areas"],
                    "version": data["area"]["version"],
                }
            )
        )
    else:
        raise ValueError("Data Managers (creating campaign): invalid placing")


async def _create_status(data: dict, campaign_id: int) -> None:
    """create an entry in status history

    :param data: dict from 'campaign.proto::CampaignData'
    """
    data = {
        "campaign_id": campaign_id,
        "status": data["status"],
        "author_id": data["author_id"],
    }
    await db.rw.execute(tables.status_history.insert(data))


async def create_campaign(data: dict) -> dict:
    async with db.rw.transaction():
        billing_id = await _create_billing(data["billing"])
        campaign_id = await _create_campaign(data, billing_id)
        await _create_week_schedule(data["week_schedule"], campaign_id)
        await _create_creatives(data["creatives"], campaign_id)
        await _create_actions(data["actions"], campaign_id)
        await _create_placing(data["placing"], campaign_id)

        await _create_status(data, campaign_id)

        campaign_data = await get_campaign(campaign_id, use_rw_conn=True)

    return {"id": campaign_id, "data": dict(campaign_data)}
