import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB

from . import constraints
from .columns import Column, DateTimeTZ, ImagesColumn, MoneyColumn
from maps_adv.adv_store.api.schemas import enums
from maps_adv.common.helpers.enums import CampaignTypeEnum

_CONSTRAINT_NAMING_CONVENTION = {
    "ix": "ix_%(table_name)s_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}


metadata = sa.MetaData(naming_convention=_CONSTRAINT_NAMING_CONVENTION)


campaign = sa.Table(
    "campaign",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("author_id", sa.BigInteger),
    Column("name", sa.UnicodeText),
    Column("publication_envs", sa.ARRAY(sa.Enum(enums.PublicationEnvEnum))),
    Column("campaign_type", sa.Enum(CampaignTypeEnum)),
    Column("start_datetime", DateTimeTZ),
    Column("end_datetime", DateTimeTZ),
    Column("timezone", sa.Text),  # e.g. 'Europe/Moscow'
    Column("billing_id", sa.BigInteger, sa.ForeignKey("campaign_billing.id")),
    Column("platforms", sa.ARRAY(sa.Enum(enums.PlatformEnum))),
    Column("order_id", sa.BigInteger, nullable=True),
    Column("manul_order_id", sa.BigInteger, nullable=True),
    Column("comment", sa.UnicodeText, server_default=""),
    Column("user_display_limit", sa.Integer, nullable=True),
    Column("user_daily_display_limit", sa.Integer, nullable=True),
    Column("targeting", JSONB, server_default="{}"),
    Column("rubric", sa.Enum(enums.RubricEnum), nullable=True),
    Column("order_size", sa.Enum(enums.OrderSizeEnum), nullable=True),
    Column("changed_datetime", DateTimeTZ, server_default=sa.func.now(), index=True),
    Column("display_probability", sa.Numeric(precision=7, scale=6), nullable=True),
    Column("display_probability_auto", sa.Numeric(precision=7, scale=6), nullable=True),
    Column("datatesting_expires_at", DateTimeTZ, nullable=True),
    Column("settings", JSONB, server_default="{}"),
    # Constraints:
    constraints.NotNegative("user_display_limit"),
    constraints.NotNegative("user_daily_display_limit"),
    constraints.NotNegative("order_id"),
    constraints.NotNegative("manul_order_id"),
    constraints.OptionalOneOf("order_id", "manul_order_id"),
    constraints.JsonbObject("targeting"),
    constraints.ArrayNotEmpty("publication_envs"),
    constraints.ArrayNotEmpty("platforms"),
    constraints.LessThan("start_datetime", "end_datetime"),
    constraints.StringNotEmpty("name"),
    constraints.StringNotEmpty("timezone"),
    constraints.IsPgTimeZoneName("timezone"),
    constraints.LessOrEqualThanValue("display_probability", 1.0),
    constraints.LessOrEqualThanValue("display_probability_auto", 1.0),
    constraints.JsonbObject("settings"),
    # Indexes
    sa.Index(
        "campaign_order_id_is_null",
        "order_id",
        postgresql_using="btree",
        postgresql_where=sa.column("order_id").is_(None),
    ),
    sa.Index(
        "campaign_manul_order_id_is_null",
        "manul_order_id",
        postgresql_using="btree",
        postgresql_where=sa.column("manul_order_id").is_(None),
    ),
)


organizations = sa.Table(
    "campaign_placing_organizations",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id"), unique=True),
    Column("permalinks", sa.ARRAY(sa.BigInteger)),
    constraints.ArrayNotEmpty("permalinks"),
    constraints.ArrayElementsPositive("permalinks"),
)


area = sa.Table(
    "campaign_placing_area",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id"), unique=True),
    Column("areas", JSONB),
    Column("version", sa.BigInteger),
    constraints.JsonbArray("areas"),
    constraints.NotNegative("version"),
)


week_schedule = sa.Table(
    "campaign_week_schedule",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("start", sa.Integer),
    Column("end", sa.Integer),
    constraints.NotNegative("start"),
    constraints.NotNegative('"end"'),
    constraints.LessThan("start", '"end"'),
    constraints.LessOrEqual("start", 7 * 24 * 60),
    constraints.LessOrEqual('"end"', 7 * 24 * 60),
    sa.Index(
        "campaign_week_schedule_campaign_id", "campaign_id", postgresql_using="hash"
    ),
)


billing = sa.Table(
    "campaign_billing",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("fix_id", sa.BigInteger, sa.ForeignKey("billing_fix.id"), nullable=True),
    Column("cpm_id", sa.BigInteger, sa.ForeignKey("billing_cpm.id"), nullable=True),
    Column("cpa_id", sa.BigInteger, sa.ForeignKey("billing_cpa.id"), nullable=True),
    constraints.OneOf("fix_id", "cpm_id", "cpa_id"),
)


fix = sa.Table(
    "billing_fix",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("time_interval", sa.Enum(enums.FixTimeIntervalEnum)),
    MoneyColumn("cost"),
    constraints.Positive("cost"),
)


cpm = sa.Table(
    "billing_cpm",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    MoneyColumn("cost"),
    MoneyColumn("budget", nullable=True),
    MoneyColumn("daily_budget", nullable=True),
    Column("auto_daily_budget", sa.Boolean, server_default="FALSE"),
    constraints.Positive("cost"),
    constraints.Positive("budget"),
    constraints.Positive("daily_budget"),
)


cpa = sa.Table(
    "billing_cpa",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    MoneyColumn("cost"),
    MoneyColumn("budget", nullable=True),
    MoneyColumn("daily_budget", nullable=True),
    Column("auto_daily_budget", sa.Boolean, server_default="FALSE"),
    constraints.Positive("cost"),
    constraints.Positive("budget"),
    constraints.Positive("daily_budget"),
)


billboard = sa.Table(
    "creative_billboard",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    ImagesColumn("images_v2"),
    Column("title", sa.UnicodeText),
    Column("description", sa.UnicodeText),
    constraints.JsonbArray("images"),
    constraints.JsonbArray("images_v2"),
    sa.Index("creative_billboard_campaign_id", "campaign_id", postgresql_using="hash"),
)


pin = sa.Table(
    "creative_pin",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("title", sa.UnicodeText),
    Column("subtitle", sa.UnicodeText),
    constraints.JsonbArray("images"),
    sa.Index("creative_pin_campaign_id", "campaign_id", postgresql_using="hash"),
)


logo_and_text = sa.Table(
    "creative_logo_and_text",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("text", sa.UnicodeText),
    constraints.JsonbArray("images"),
    sa.Index(
        "creative_logo_and_text_campaign_id", "campaign_id", postgresql_using="hash"
    ),
)


banner = sa.Table(
    "creative_banner",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("disclaimer", sa.UnicodeText),
    Column("show_ads_label", sa.Boolean),
    Column("title", sa.UnicodeText),
    Column("description", sa.UnicodeText),
    Column("terms", sa.UnicodeText, server_default=""),
    constraints.JsonbArray("images"),
    sa.Index("creative_banner_campaign_id", "campaign_id", postgresql_using="hash"),
)


text = sa.Table(
    "creative_text",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("text", sa.UnicodeText),
    Column("disclaimer", sa.UnicodeText),
    sa.Index("creative_text_campaign_id", "campaign_id", postgresql_using="hash"),
)


icon = sa.Table(
    "creative_icon",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("title", sa.UnicodeText),
    Column("position", sa.Integer),
    constraints.JsonbArray("images"),
    sa.Index("creative_icon_campaign_id", "campaign_id", postgresql_using="hash"),
)


pin_search = sa.Table(
    "creative_pin_search",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("title", sa.UnicodeText),
    Column("organizations", sa.ARRAY(sa.BigInteger)),
    constraints.JsonbArray("images"),
    constraints.ArrayNotEmpty("organizations"),
    constraints.ArrayElementsPositive("organizations"),
    sa.Index("creative_pin_search_campaign_id", "campaign_id", postgresql_using="hash"),
)

via_point = sa.Table(
    "creative_via_point",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("button_text_active", sa.UnicodeText),
    Column("button_text_inactive", sa.UnicodeText),
    Column("description", sa.UnicodeText),
    constraints.JsonbArray("images"),
    sa.Index("creative_via_point_campaign_id", "campaign_id", postgresql_using="hash"),
)

audio_banner = sa.Table(
    "creative_audio_banner",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    ImagesColumn("images"),
    Column("left_anchor", sa.Numeric(precision=7, scale=6)),
    Column("audio_file_url", sa.UnicodeText),
    constraints.JsonbArray("images"),
    constraints.NotNegative("left_anchor"),
    constraints.LessOrEqualThanValue("left_anchor", 1),
    sa.Index(
        "creative_audio_banner_campaign_id", "campaign_id", postgresql_using="hash"
    ),
)

open_site = sa.Table(
    "action_open_site",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("title", sa.UnicodeText, nullable=True),
    Column("url", sa.UnicodeText),
    Column("main", sa.Boolean, server_default="FALSE"),
    sa.Index("action_open_site_campaign_id", "campaign_id", postgresql_using="hash"),
)


search = sa.Table(
    "action_search",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("title", sa.UnicodeText, nullable=True),
    Column("organizations", sa.ARRAY(sa.BigInteger)),
    Column("history_text", sa.UnicodeText),
    Column("main", sa.Boolean, server_default="FALSE"),
    constraints.ArrayNotEmpty("organizations"),
    constraints.ArrayElementsPositive("organizations"),
    sa.Index("action_search_campaign_id", "campaign_id", postgresql_using="hash"),
)


phone_call = sa.Table(
    "action_phone_call",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("title", sa.UnicodeText, nullable=True),
    Column("phone", sa.UnicodeText),
    Column("main", sa.Boolean, server_default="FALSE"),
    sa.Index("action_phone_call_campaign_id", "campaign_id", postgresql_using="hash"),
)


download_app = sa.Table(
    "action_download_app",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("title", sa.UnicodeText, nullable=True),
    Column("google_play_id", sa.UnicodeText, nullable=True),
    Column("app_store_id", sa.UnicodeText, nullable=True),
    Column("url", sa.UnicodeText),
    Column("main", sa.Boolean, server_default="FALSE"),
    sa.Index("action_download_app_campaign_id", "campaign_id", postgresql_using="hash"),
)


promocode = sa.Table(
    "action_promocode",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("promocode", sa.UnicodeText),
    Column("main", sa.Boolean, server_default="FALSE"),
    sa.Index("action_promocode_campaign_id", "campaign_id", postgresql_using="hash"),
)


resolve_uri = sa.Table(
    "action_resolve_uri",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("uri", sa.UnicodeText),
    Column("action_type", sa.Enum(enums.ActionTypeEnum)),
    Column("target", sa.Enum(enums.ResolveUriTargetEnum), server_default="WEB_VIEW"),
    Column("dialog", JSONB, nullable=True),
    Column("main", sa.Boolean, server_default="FALSE"),
    constraints.JsonbObject("dialog"),
    sa.Index("action_resolve_uri_campaign_id", "campaign_id", postgresql_using="hash"),
)


add_point_to_route = sa.Table(
    "action_add_point_to_route",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("latitude", sa.Float),
    Column("longitude", sa.Float),
    Column("main", sa.Boolean, server_default="FALSE"),
    sa.Index(
        "action_add_point_to_route_campaign_id", "campaign_id", postgresql_using="hash"
    ),
)


status_history = sa.Table(
    "status_history",
    metadata,
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("author_id", sa.BigInteger),
    Column("status", sa.Enum(enums.CampaignStatusEnum)),
    Column("metadata", JSONB, server_default="{}"),
    Column("changed_datetime", DateTimeTZ, server_default=sa.func.now()),
    constraints.JsonbObject("metadata"),
)
sa.Index(
    "status_history_campaign_changed_datetime",
    status_history.c.campaign_id.desc(),
    status_history.c.changed_datetime.desc(),
    postgresql_using="btree",
),

campaigns_activity_log = sa.Table(
    "campaigns_activity_log",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("created_at", DateTimeTZ, server_default=sa.func.now()),
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("author_id", sa.BigInteger, nullable=True),
    Column("status", sa.Enum(enums.CampaignStatusEnum)),
    Column("daily_budget_reached_due_to", DateTimeTZ, nullable=True),
    MoneyColumn("daily_budget_reached_value", nullable=True),
    MoneyColumn("budget_reached_value", nullable=True),
    Column("order_limit_reached", sa.Boolean),
    Column("metadata", JSONB, server_default="{}"),
    Column("system_metadata", JSONB, server_default="{}"),
    Column("is_latest", sa.Boolean),
    constraints.JsonbObject("metadata"),
    constraints.JsonbObject("system_metadata"),
    sa.Index(
        "campaign_id_hash_partial_is_latest",
        "campaign_id",
        postgresql_using="btree",
        postgresql_where=sa.column("is_latest"),
    ),
)

discounts = sa.Table(
    "campaign_discounts",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("start_datetime", DateTimeTZ),
    Column("end_datetime", DateTimeTZ),
    Column("cost_multiplier", sa.Numeric(precision=8, scale=4)),
    constraints.LessThan("start_datetime", "end_datetime"),
    sa.Index("campaign_discounts_campaign_id", "campaign_id", postgresql_using="hash"),
)

campaign_event = sa.Table(
    "campaign_event",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("timestamp", DateTimeTZ),
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("event_type", sa.Enum(enums.CampaignEventTypeEnum)),
    Column("event_data", JSONB, server_default="{}"),
    constraints.JsonbObject("event_data"),
    sa.Index("campaign_event_timestamp", "campaign_id"),
    sa.Index("campaign_event_campaign_id", "campaign_id", postgresql_using="hash"),
)

campaign_history = sa.Table(
    "campaigns_change_log",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("created_at", DateTimeTZ, server_default=sa.func.now()),
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("author_id", sa.BigInteger, nullable=True),
    Column("status", sa.Enum(enums.CampaignStatusEnum)),
    Column("system_metadata", JSONB, server_default="{}"),
    Column("state_before", JSONB, server_default="{}"),
    Column("state_after", JSONB, server_default="{}"),
    Column("is_latest", sa.Boolean),
    constraints.JsonbObject("state_before"),
    constraints.JsonbObject("state_after"),
    sa.Index(
        "ix_campaigns_change_log_campaign_id_hash_partial_is_latest",
        "campaign_id",
        postgresql_using="btree",
        postgresql_where=sa.column("is_latest"),
    ),
    sa.Index("campaigns_change_log_created_at", "created_at", postgresql_using="btree"),
)

campaign_moderation = sa.Table(
    "campaign_moderation",
    metadata,
    Column("id", sa.BigInteger, primary_key=True),
    Column("created_at", DateTimeTZ),
    Column("campaign_id", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("reviewer_uid", sa.BigInteger, sa.ForeignKey("campaign.id")),
    Column("status", sa.Enum(enums.CampaignDirectModerationStatusEnum)),
    Column("workflow", sa.Enum(enums.CampaignDirectModerationWorkflowEnum)),
    Column("verdicts", sa.ARRAY(sa.BigInteger)),
    sa.Index("campaign_moderation_statis", "status", postgresql_using="hash"),
    sa.Index("campaign_moderation_campaign_id", "campaign_id", postgresql_using="hash"),
)
