from __future__ import absolute_import
from flask_sqlalchemy.query import Query as BaseQuery
from sqlalchemy import func, CheckConstraint
from sqlalchemy import and_
from dostadmin import db
from dostadmin.mixins import TimestampMixin


class MessageCampaignQuery(BaseQuery):
    def get_sms_campaigns_log(self):
        return self.all()

    def get_sms_campaigns_for_date(self, ip_date):
        return self.filter(func.DATE(MessageCampaign.deploy_datetime) == ip_date).all()

    def get_sms_campaign_by_id(self, campaign_id):
        return self.filter(MessageCampaign.id == campaign_id).first()

    def get_message_campaigns_with_status(self, status):
        return self.filter(MessageCampaign.status == status).all()

    def get_whatsapp_message_with_status_and_date(self, status, deploy_date):
        return self.filter(
            and_(
                MessageCampaign.status == status,
                func.DATE(MessageCampaign.deploy_datetime) == deploy_date,
            )
        ).first()

    def get_wa_message_with_sid(self, sid):
        return self.filter(MessageCampaign.sid == sid).first()

    def get_sms_campaigns_with_sms(self, sms_id):
        return self.filter(MessageCampaign.message_id == sms_id).all()


class MessageCampaign(TimestampMixin, db.Model):
    __tablename__ = "messagecampaign"
    query_class = MessageCampaignQuery

    # https://developer.exotel.com/api/#sms-details
    class Status:
        SCHEDULED = "scheduled"
        SENT = "sent"
        FAILED = "failed"
        FAILED_DND = "failed_dnd"
        QUEUED = "queued"
        SENDING = "sending"
        SUBMITTED = "submitted"
        FAILED_SYSTEM = "failed_system"
        FAILED_OUTOFWINDOW = "failed_outofwindow"
        FAILED_INVALIDWA = "failed_invalidwa"
        READ = "read"
        DELIVERED = "delivered"
        DELETED = "deleted"

        CHOICE = (
            (SCHEDULED, "Scheduled"),
            (SENT, "Sent"),
            (FAILED, "Failed"),
            (FAILED_DND, "Failed DND"),
            (QUEUED, "Queued"),
            (SENDING, "Sending"),
            (SUBMITTED, "Submitted"),
            (FAILED_SYSTEM, "Failed System"),
            (FAILED_OUTOFWINDOW, "Failed Out of Window"),
        )

    class ScheduledBy:
        CRON_REGULAR = "cron_regular"
        UI_MANUAL = "ui_manual"

        CHOICE = (
            (CRON_REGULAR, "Cron Regular"),
            (UI_MANUAL, "UI Manual"),
        )

    class WAPhone:
        WA_1_INFOBIP = "911171279658"
        WA_2_TURN = "911171279788"

        CHOICE = ((WA_2_TURN, "Whatsapp Turn Account"),)

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    experience_id = db.Column(db.Integer, db.ForeignKey("experience.id"))
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    message_id = db.Column(db.Integer, db.ForeignKey("message.id"), nullable=False)
    provider_number = db.Column(db.String(20), nullable=False)
    user_number = db.Column(db.String(20), nullable=False)
    deploy_datetime = db.Column(db.DateTime, nullable=False)
    attempted_timestamp = db.Column(db.DateTime)
    scheduled_by = db.Column(db.String(30), nullable=False)
    sid = db.Column(db.String(50))
    status = db.Column(db.String(30), nullable=False)
    detailed_status = db.Column(db.String(1000))
    detailed_status_code = db.Column(db.String(30))
    sent_time = db.Column(db.DateTime)
    uri = db.Column(db.String(1000))
    direction = db.Column(db.String(50))
    params = db.Column(db.String(3000))

    __table_args__ = (
        CheckConstraint(
            str(provider_number).isdigit() is True, name="check_provider_number_isdigit"
        ),
        {},
    )

    def __repr__(self):
        return (
            "\n SMSCampaign: id_ "
            + str(self.id)
            + ", time: "
            + str(self.deploy_datetime)
            + ", Status: "
            + self.status
        )

    def update_sms_campaign_after_callback(
        self, sid, status, detailed_status, detailed_status_code, sent_time
    ):
        self.sid = sid
        self.status = status
        self.detailed_status = detailed_status
        self.detailed_status_code = detailed_status_code
        self.sent_time = sent_time
        db.session.commit()
        return status

    @classmethod
    def update_message_status(cls, event_data):
        status_notification = event_data.get("statuses")
        if status_notification:
            message_id = status_notification[0].get("id")
            message_campaign = MessageCampaign.query.get_wa_message_with_sid(message_id)
            if message_campaign:
                status = status_notification[0].get("status")
                if status == MessageCampaign.Status.SENT:
                    if message_campaign.status not in [
                        MessageCampaign.Status.READ,
                        MessageCampaign.Status.DELIVERED,
                    ]:
                        message_campaign.status = status
                elif status == MessageCampaign.Status.DELIVERED:
                    if message_campaign.status not in [MessageCampaign.Status.READ]:
                        message_campaign.status = status
                else:
                    message_campaign.status = status

                db.session.commit()
