# -*- coding: utf-8 -*-
from __future__ import absolute_import
from datetime import date
from utils.helpers.helpers import get_current_isttime
from flask_sqlalchemy.query import Query as BaseQuery
from sqlalchemy import desc, func, CheckConstraint, and_, or_
from dostadmin import db, app, app_logger
from dostadmin.db_model.experience import Experience
from dostadmin.mixins import TimestampMixin


class CampaignQuery(BaseQuery):
    def get_campaigns_log(self):
        return self.all()

    def get_campaigns_for_date(self, ip_date, current_page, per_page=30):
        return self.filter(func.DATE(Campaign.deploy_datetime) == ip_date).paginate(
            page=current_page, per_page=per_page, error_out=False
        )

    def get_campaign_by_id(self, campaign_id):
        return self.filter(Campaign.id == campaign_id).first()

    def get_campaigns_with_status(self, status):
        return self.filter(Campaign.status == status).all()

    def get_last_campaign_with_pseq_for_experience(self, experience_id):
        return (
            self.filter(
                and_(
                    Campaign.experience_id == experience_id,
                    Campaign.programseq_id.is_not(None),
                )
            )
            .order_by(desc(Campaign.deploy_datetime))
            .first()
        )

    def get_last_campaign_by_experience(self, experience_id):
        return (
            self.filter(Campaign.experience_id == experience_id)
            .order_by(desc(Campaign.deploy_datetime))
            .first()
        )

    def get_last_campaign_by_user_id(self, user_id):
        return (
            self.filter(Campaign.user_id == user_id).order_by(desc(Campaign.id)).first()
        )

    def get_live_calls_today_agent(self, agent_number):
        return self.filter(
            and_(
                func.DATE(Campaign.deploy_datetime) == date.today(),
                Campaign.scheduled_by == Campaign.ScheduledBy.LIVE_CALL,
                Campaign.agent_number == agent_number,
            )
        ).all()

    def get_today_campaign_to_call(self, scheduled_by_filter):
        ist_now = get_current_isttime()
        campaigns = self.filter(
            and_(
                func.DATE(Campaign.deploy_datetime) == date.today(),
                Campaign.status == Campaign.Status.SCHEDULED,
                Campaign.deploy_datetime <= ist_now,
                or_(
                    *(
                        Campaign.scheduled_by == scheduled_by
                        for scheduled_by in scheduled_by_filter
                    )
                ),
            )
        ).all()

        return campaigns

    def get_confirmation_call_for_experience(
        self,
        experience_id,
        content_id=app.config["CONFIRMATION_CALL_CONTENT_ID"],
    ):
        return (
            self.filter(
                and_(
                    Campaign.experience_id == experience_id,
                    Campaign.content_id == content_id,
                )
            )
            .order_by(Campaign.deploy_datetime)
            .first()
        )


class CustomizedCampaign:
    class Action:
        NOTHING = "nothing"
        ADD_CAMPAIGN = "add_campaign"
        UPDATE_deploy_datetime_TO_NOW = "update_deploy_datetime_to_now"

    def __init__(self, data):
        self.name = data.get("name", "")
        self.content_id = data.get("content_id")
        self.content_version_id = data.get("content_version_id")
        self.experience_id = data.get("experience_id")
        self.deploy_datetime = data.get("deploy_datetime")
        self.status = data.get("status", "")
        self.scheduled_by = data.get("scheduled_by", "")
        self.user_id = data.get("user_id")
        self.user_number = data.get("user_number")
        self.id = data.get("id")
        self.program_id = data.get("program_id")
        self.timecategory_id = data.get("timecategory_id")
        self.attempted_timestamp = data.get("attempted_timestamp")
        self.provider_number = data.get("provider_number")
        self.programseq_id = data.get("programseq_id")
        self.parent_campaign_id = data.get("parent_campaign_id", None)
        self.parent_campaign_deploy_time = data.get("parent_campaign_deploy_time", None)


class Campaign(TimestampMixin, db.Model):
    __tablename__ = "campaign"
    query_class = CampaignQuery

    class Status:
        SCHEDULED = "scheduled"
        ATTEMPTED = "attempted"
        IN_PROGRESS = "in-progress"
        FAILED_NOSCHEDULE = "failed_noschedule"
        FAILED_NOCONNECT = "failed_noconnect"
        FAILED_NORESPONSE = "failed_noresponse"
        COMPLETED = "completed"
        DELETED = "deleted"
        UNSCHEDULED = "unscheduled"
        QUEUED = "queued"
        FAILED_SYSTEM = "failed_system"
        FAILED_DND = "failed_dnd"
        FAILED_OUTOFWINDOW = "failed_outofwindow"
        FAILED_TOOMANYREQUESTS = "failed_toomanyrequests"

        CHOICE = (
            (SCHEDULED, "Scheduled"),
            (ATTEMPTED, "Attempted"),
            (FAILED_NOSCHEDULE, "Failed No Schedule"),
            (FAILED_NOCONNECT, "Failed No Connect"),
            (FAILED_NORESPONSE, "Failed No Response"),
            (COMPLETED, "Completed"),
            (DELETED, "Deleted"),
            (UNSCHEDULED, "Unscheduled"),
            (QUEUED, "Queued"),
            (FAILED_SYSTEM, "Failed System"),
            (FAILED_DND, "Failed DND"),
            (FAILED_OUTOFWINDOW, "Failed Out Of Window"),
        )

    class ScheduledBy:
        CRON_REGULAR = "cron_regular"
        CRON_MISSED_CALL = "cron_missed_call"
        UI_MANUAL = "ui_manual"
        CRON_NUDGE = "cron_nudge"
        LIVE_CALL = "live_call"
        FEEDBACK_CALL = "feedback_call"
        CHAMPION_CALL = "champion_call"

        CHOICE = (
            (CRON_REGULAR, "Cron Regular"),
            (CRON_MISSED_CALL, "Cron Missed Call"),
            (UI_MANUAL, "UI Manual"),
            (LIVE_CALL, "Live Call"),
            (FEEDBACK_CALL, "Feedback Call"),
            (CHAMPION_CALL, "Champion Call"),
        )

    class ExotelCallbackStatus:
        NO_ANSWER = "no-answer"
        BUSY = "busy"
        COMPLETED = "completed"
        FAILED = "failed"
        IN_PROGRESS = "in-progress"

        CONNECT_FAIL = [NO_ANSWER, BUSY, FAILED]
        CONNECT_SUCCESS = COMPLETED

    class FailNoScheduleStatus:
        SYSTEM_ERROR = "system_error"
        DND_ERROR = "dnd_error"
        WINDOW_ERROR = "window_error"

    class LiveCallPurpose:
        REFERRAL = "REFERRAL"
        SURVEY = "SURVEY"
        PRODUCT_RESEARCH = "PROD_RES"
        SIGNUP = "SIGNUP"
        COUNSELING = "COUNSELING"

        CHOICE_ENGLISH = (
            (COUNSELING, "Counseling"),
            (SURVEY, "Survey"),
            (PRODUCT_RESEARCH, "Product Research"),
            (SIGNUP, "Signup User"),
            (REFERRAL, "Referral User"),
        )

        CHOICE_HINDI = (
            (REFERRAL, "यूज़र रेफरल"),
            (PRODUCT_RESEARCH, "प्रॉडक्ट की जानकारी"),
            (SURVEY, "सर्वे"),
            (SIGNUP, "यूज़र साइनअप"),
        )

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    content_id = db.Column(db.Integer, db.ForeignKey("content.id"))
    content_version_id = db.Column(db.Integer, db.ForeignKey("content_version.id"))
    deploy_datetime = db.Column(db.DateTime)
    experience_id = db.Column(db.Integer, db.ForeignKey("experience.id"))
    status = db.Column(db.String(30))
    scheduled_by = db.Column(db.String(30))
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    program_id = db.Column(db.Integer)
    parent_campaign_id = db.Column(db.Integer)
    parent_campaign_deploy_time = db.Column(db.DateTime)
    programseq_id = db.Column(db.Integer)
    timecategory_id = db.Column(db.Integer)
    attempted_timestamp = db.Column(db.DateTime)
    callsid = db.Column(db.String(50))
    provider_name = db.Column(db.String(20))
    provider_number = db.Column(db.String(20))
    agent_number = db.Column(db.String(20))
    user_number = db.Column(db.String(20))
    # not giving it a choice field because exotel could change it
    call_status = db.Column(db.String(30))
    call_start_time = db.Column(db.DateTime)
    call_end_time = db.Column(db.DateTime)
    listen_secs = db.Column(db.Integer)
    price = db.Column(db.Float)
    provider_circle = db.Column(db.String(20))
    user_circle = db.Column(db.String(20))
    recording_url = db.Column(db.String(200))

    content_version = db.relationship(
        "ContentVersion", back_populates="campaign", lazy="joined"
    )
    experience = db.relationship("Experience", lazy="joined")

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

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

    def update_campaign_after_callback(self, status, callsid):
        call_status = Campaign.get_campaign_status_from_exotel_status(status)
        if call_status:
            self.status = call_status
            self.callsid = callsid
            db.session.commit()
        return call_status

    def mark_campaign_experience_as_paused(self):
        experience = self.experience
        experience.status = Experience.Status.PAUSED
        db.session.commit()

    @classmethod
    def get_campaign_status_from_exotel_status(cls, exotel_status):
        call_status = ""
        if exotel_status == Campaign.ExotelCallbackStatus.COMPLETED:
            call_status = Campaign.Status.COMPLETED
        elif exotel_status in (
            Campaign.ExotelCallbackStatus.NO_ANSWER,
            Campaign.ExotelCallbackStatus.BUSY,
        ):
            call_status = Campaign.Status.FAILED_NORESPONSE
        elif exotel_status == Campaign.ExotelCallbackStatus.FAILED:
            call_status = Campaign.Status.FAILED_NOCONNECT

        return call_status

    def change_campaign_status(self, status):
        if self.status not in ("completed", "deleted", "failed"):
            self.status = status
            db.session.commit()
        else:
            app_logger.error("Cannot edit completed/deleted campaign %d", self.id)
        return self

    @classmethod
    def delete_campaign(cls, cp_id):
        campaign = Campaign.query.get_campaign_by_id(cp_id)
        if campaign:
            campaign.change_campaign_status(Campaign.Status.DELETED)

    @classmethod
    def delete_campaigns_for_experience_id(cls, exp_id):
        try:
            campaign_logs = cls.query.filter_by(experience_id=exp_id).all()
            for campaign in campaign_logs:
                db.session.delete(campaign)
            db.session.commit()
        except Exception as e:
            return str(e)
