from dostadmin import db, app, app_logger
from dostadmin.db_model import Experience, Campaign, ProviderNumber, ContentVersion
from dostadmin.services.scheduling.pre_calculated_campaign_service import (
    PreCalculatedCampaignService,
)
from datetime import datetime, timedelta, date
from utils.helpers.helpers import (
    get_current_isttime,
    replace_chars,
    get_current_utc_time,
)


class CampaignCreationService:
    def get_special_content_ids(self, exclude_provider_number_table_contents=False):
        special_content_ids = []

        if not exclude_provider_number_table_contents:
            for provider_number in ProviderNumber.query.distinct(
                ProviderNumber.content_id
            ):
                special_content_ids.append(provider_number.content_id)

        special_content_ids.append(int(app.config["CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["UNSUBSCRIBE_CALLBACK_CONTENT_ID"]))
        special_content_ids.append(int(app.config["ASSAM_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["ASSAM_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BHOJHPURI_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BHOJHPURI_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BIHAR_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BIHAR_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["MP_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["MP_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BHOJPUR_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["BHOJPUR_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["NAWADA_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["NAWADA_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(
            int(app.config["UNSUBSCRIBE_CALLBACK_CONTENT_ID_FOR_ASSAM"])
        )
        special_content_ids.append(int(app.config["JHARKHAND_CALLBACK_2_CONTENT_ID"]))
        special_content_ids.append(int(app.config["JHARKHAND_CALLBACK_3_CONTENT_ID"]))
        special_content_ids.append(int(app.config["UI_ONBOARDING_CALL_CONTENT_ID"]))

        return special_content_ids

    def can_retry(
        self,
        scheduled_time,
        previous_call_status,
        is_previous_call_dialed_today,
        experience_type=Experience.Type.SELFPACEDPC,
        scheduled_by=Campaign.ScheduledBy.CRON_REGULAR,
    ):
        if (
            previous_call_status == Campaign.Status.SCHEDULED
            and is_previous_call_dialed_today
        ):
            return False

        is_self_paced = experience_type == Experience.Type.SELFPACEDPC

        istnow = get_current_isttime()
        now_hm = istnow.time()
        retry_window = int(app.config["CAMPAIGN_RETRY_TIME_WINDOW"])

        if scheduled_by == Campaign.ScheduledBy.CRON_MISSED_CALL:
            retry_window = int(app.config["MISSED_CALL_RETRY_WINDOW_IN_SECONDS"])
        elif scheduled_by == Campaign.ScheduledBy.CRON_NUDGE:
            retry_window = int(app.config["NUDGE_RETRY_TIME_WINDOW_IN_SECONDS"])
        elif scheduled_by == Campaign.ScheduledBy.UI_MANUAL:
            retry_window = int(app.config["BLAST_RETRY_TIME_WINDOW_IN_SECONDS"])

        scheduled_time = datetime.strptime(scheduled_time, "%H:%M").time()
        diff = datetime.combine(date.today(), now_hm) - datetime.combine(
            date.today(), scheduled_time
        )

        if scheduled_by == Campaign.ScheduledBy.CRON_REGULAR:
            # 3 retries max
            time_difference = diff.total_seconds() / 3600

            if not is_self_paced:
                # 2 retries max
                time_difference += 1
        else:
            time_difference = diff.total_seconds()

        return 0 < time_difference < retry_window

    def add_new_campaign(self, cursor, new_campaign):
        new_campaign_id = -1
        if new_campaign.programseq_id:
            insert_sql = (
                "INSERT INTO campaign(name, content_id, experience_id, "
                "deploy_datetime, status, scheduled_by, timecategory_id, content_version_id, "
                "program_id, user_id, provider_number, programseq_id, created_on, "
                "updated_on, parent_campaign_deploy_time, parent_campaign_id) "
                "VALUES("
                f"'{new_campaign.name}', "
                f"'{new_campaign.content_id}', "
                f"'{new_campaign.experience_id}', "
                f"'{datetime.strftime(new_campaign.deploy_datetime, '%Y-%m-%d %H:%M:%S')}', "
                f"'{new_campaign.status}', "
                f"'{new_campaign.scheduled_by}', "
                f"'{new_campaign.timecategory_id}', "
                f"'{new_campaign.content_version_id}', "
                f"'{new_campaign.program_id}', "
                f"'{new_campaign.user_id}', "
                f"'{new_campaign.provider_number}', "
                f"'{new_campaign.programseq_id}', "
                f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
                f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
                f"{new_campaign.parent_campaign_deploy_time if new_campaign.parent_campaign_deploy_time else 'null'}, "
                f"{new_campaign.parent_campaign_id if new_campaign.parent_campaign_id else 'null'}"
                ") "
                "RETURNING id"
            )
        else:
            insert_sql = (
                "INSERT INTO campaign(name, content_id, experience_id, "
                "deploy_datetime, status, scheduled_by, timecategory_id, content_version_id, "
                "program_id, user_id, provider_number, created_on, "
                "updated_on, parent_campaign_deploy_time, parent_campaign_id) "
                "VALUES("
                f"'{new_campaign.name}', "
                f"'{new_campaign.content_id}', "
                f"'{new_campaign.experience_id}', "
                f"'{datetime.strftime(new_campaign.deploy_datetime, '%Y-%m-%d %H:%M:%S')}', "
                f"'{new_campaign.status}', "
                f"'{new_campaign.scheduled_by}', "
                f"'{new_campaign.timecategory_id}', "
                f"'{new_campaign.content_version_id}', "
                f"'{new_campaign.program_id}', "
                f"'{new_campaign.user_id}', "
                f"'{new_campaign.provider_number}', "
                f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
                f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
                f"{new_campaign.parent_campaign_deploy_time if new_campaign.parent_campaign_deploy_time else 'null'}, "
                f"{new_campaign.parent_campaign_id if new_campaign.parent_campaign_id else 'null'}"
                ") "
                "RETURNING id"
            )
        try:
            cursor.execute(insert_sql)
            new_campaign_id = cursor.fetchone()[0]

            campaign_data = {
                "campaign_id": new_campaign_id,
                "campaign_name": new_campaign.name,
                "experience_id": new_campaign.experience_id,
                "deploy_datetime": datetime.strftime(
                    new_campaign.deploy_datetime, "%Y-%m-%d %H:%M:%S"
                ),
                "content_id": new_campaign.content_id,
                "content_version_id": new_campaign.content_version_id,
                "scheduled_by": new_campaign.scheduled_by,
            }
            PreCalculatedCampaignService().update_pre_calculated_user_campaign_data(
                campaign_data
            )

        except Exception as error:
            cursor.execute("ROLLBACK")
            app_logger.error(
                f"Campaign Creator Job: Unable to write add new campaign query to the database: {insert_sql}. Error message: {error}"
            )
        return new_campaign_id

    def add_new_blast_campaign(self, cursor, new_campaign):
        new_campaign_id = -1
        insert_sql = (
            "INSERT INTO campaign("
            "name, content_id, experience_id, deploy_datetime, status, scheduled_by, "
            "user_id, content_version_id, provider_number, created_on, updated_on, "
            "parent_campaign_id, parent_campaign_deploy_time) "
            "VALUES("
            f"'{new_campaign.name}', "
            f"'{new_campaign.content_id}', "
            f"'{new_campaign.experience_id}', "
            f"'{new_campaign.deploy_datetime}', "
            f"'{new_campaign.status}', "
            f"'{new_campaign.scheduled_by}', "
            f"'{new_campaign.user_id}', "
            f"'{new_campaign.content_version_id}', "
            f"'{new_campaign.provider_number}', "
            f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
            f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
            f"{new_campaign.parent_campaign_id if new_campaign.parent_campaign_id else 'null'}, "
            f"{new_campaign.parent_campaign_deploy_time if new_campaign.parent_campaign_deploy_time else 'null'}"
            ") "
            "RETURNING id"
        )
        try:
            cursor.execute(insert_sql)
            new_campaign_id = cursor.fetchone()[0]
            campaign_data = {
                "campaign_id": new_campaign_id,
                "campaign_name": new_campaign.name,
                "experience_id": new_campaign.experience_id,
                "deploy_datetime": datetime.strftime(
                    new_campaign.deploy_datetime, "%Y-%m-%d %H:%M:%S"
                ),
                "content_id": new_campaign.content_id,
                "content_version_id": new_campaign.content_version_id,
                "scheduled_by": new_campaign.scheduled_by,
            }
            PreCalculatedCampaignService().update_pre_calculated_user_campaign_data(
                campaign_data
            )

        except Exception as error:
            cursor.execute("ROLLBACK")
            app_logger.error(
                f"Campaign Creator Job: Unable to write add new blast campaign query to the database: {insert_sql}. Error message: {error}"
            )

        return new_campaign_id

    def add_nudge_reattempt_campaign(
        self, cursor, new_campaign, nudge_id, group_version_id
    ):
        new_campaign_id = -1
        nudge_insert_sql = (
            "INSERT INTO campaign(name, content_id, experience_id, "
            "deploy_datetime, status, scheduled_by, user_id, user_number, timecategory_id, content_version_id, "
            "provider_number, created_on, updated_on, parent_campaign_deploy_time, parent_campaign_id) "
            "VALUES("
            f"'{new_campaign.name}', "
            f"'{new_campaign.content_id}', "
            f"'{new_campaign.experience_id}', "
            f"'{datetime.strftime(get_current_isttime(), '%Y-%m-%d %H:%M:%S.%f')}', "
            f"'{new_campaign.status}', "
            f"'{new_campaign.scheduled_by}', "
            f"'{new_campaign.user_id}', "
            f"'{new_campaign.user_number}', "
            f"'{new_campaign.timecategory_id}', "
            f"'{new_campaign.content_version_id}', "
            f"'{new_campaign.provider_number}', "
            f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
            f"'{datetime.strftime(get_current_utc_time(), '%Y-%m-%d %H:%M:%S.%f')}', "
            f"{new_campaign.parent_campaign_deploy_time if new_campaign.parent_campaign_deploy_time else 'null'}, "
            f"{new_campaign.parent_campaign_id if new_campaign.parent_campaign_id else 'null'}"
            ") "
            "RETURNING id"
        )
        try:
            cursor.execute(nudge_insert_sql)
            new_campaign_id = cursor.fetchone()[0]
            campaign_data = {
                "campaign_id": new_campaign_id,
                "campaign_name": new_campaign.name,
                "experience_id": new_campaign.experience_id,
                "deploy_datetime": datetime.strftime(
                    new_campaign.deploy_datetime, "%Y-%m-%d %H:%M:%S"
                ),
                "content_id": new_campaign.content_id,
                "content_version_id": new_campaign.content_version_id,
                "scheduled_by": new_campaign.scheduled_by,
            }
            PreCalculatedCampaignService().update_pre_calculated_user_campaign_data(
                campaign_data
            )

        except Exception as error:
            cursor.execute("ROLLBACK")
            app_logger.error(
                f"Campaign Creator Job: Unable to write add nudge reattempt query to the database: {nudge_insert_sql}. Error message: {error}"
            )

            return new_campaign_id

        try:
            nudge_campaign_mapping_insert_query = f"""
                INSERT INTO campaign_nudge_mapping(nudge_id, campaign_id, group_version_id,
                created_on, updated_on)
                VALUES({nudge_id}, {new_campaign_id}, {group_version_id},
                '{get_current_utc_time().strftime('%Y-%m-%d %H:%M:%S.%f')}',
                '{get_current_utc_time().strftime('%Y-%m-%d %H:%M:%S.%f')}')
                RETURNING id
            """

            cursor.execute(nudge_campaign_mapping_insert_query)
        except Exception as error:
            cursor.execute("ROLLBACK")
            app_logger.error(
                f"Campaign Creator Job: Unable to write nudge_campaign_mapping query to the database:{nudge_insert_sql}. Error message: {error}"
            )

        return new_campaign_id

    def get_deploydate_for_selfpaced(self, prev_date, deploy_time, next_pseq):
        """For weeks 1-16, calls can be scheduled any day (weekdays + weekend).
        For weeks 17 onwards, calls should go out till Thursday.
        """
        today = get_current_isttime().date()

        if next_pseq.week in range(1, 17) or (today.weekday() < 4):
            deploy_date = today
        else:
            days_for_next_monday = 7 - prev_date.weekday()
            deploy_date = prev_date + timedelta(days=days_for_next_monday)

        return datetime.strptime(
            deploy_date.strftime("%Y-%m-%d") + " " + deploy_time + ":00",
            "%Y-%m-%d %H:%M:%S",
        )

    def get_campaign_name(self, old_name):
        name_sequence = old_name.split("_")
        if len(name_sequence) == 2:
            new_name = old_name + "_1"
        else:
            next_sequence = int(name_sequence[-1]) + 1
            name_sequence[-1] = str(next_sequence)
            new_name = "_".join(name_sequence)

        return new_name

    def get_campaign_name_for_intro_calls(self, previous_campaign_name, user_id):
        if previous_campaign_name.find("W0D0_") != -1:
            return f"W0D1_{user_id}"
        if previous_campaign_name.find("W1D") != -1:
            return f"W0D2_{user_id}"
        return None

    def get_campaign_name_from_program_sequence_and_user_id(
        self, program_sequence, user_id
    ):
        week_number = program_sequence.week
        day_number = program_sequence.day

        return f"W{week_number}D{day_number}_{user_id}"

    def add_blast_campaign(self, cp_name, content_id, exp_list):
        for exp_rec in exp_list:
            language_id = exp_rec[0].language_id
            content_version = (
                ContentVersion.query.get_content_version_by_content_and_language_id(
                    content_id, language_id
                )
            )
            campaign_name = replace_chars(cp_name)
            campaign_rec = Campaign(
                name=campaign_name,
                content_id=content_id,
                content_version_id=content_version.id,
                deploy_datetime=exp_rec[1],
                status=Campaign.Status.SCHEDULED,
                experience_id=exp_rec[0].id,
                scheduled_by=Campaign.ScheduledBy.UI_MANUAL,
                user_id=exp_rec[0].user_id,
                provider_number=exp_rec[0].provider_number,
                user_number=exp_rec[0].phone,
            )
            db.session.add(campaign_rec)
            db.session.flush()

            campaign_data = {
                "experience_id": exp_rec[0].id,
                "scheduled_by": Campaign.ScheduledBy.UI_MANUAL,
                "content_id": content_id,
                "content_version_id": content_version.id,
                "deploy_datetime": exp_rec[1],
                "campaign_status": Campaign.Status.SCHEDULED,
                "campaign_name": campaign_name,
            }
            PreCalculatedCampaignService().update_pre_calculated_user_campaign_data(
                campaign_data
            )
        db.session.commit()
