from flask import Blueprint, request
import logging
import uuid
import pytz
import os
from firebase.firebase import Firebase
from datetime import datetime
from utility.function import Function
from utility.token import Token
from pydantic import BaseModel, validator,ValidationError, Field, TypeAdapter
from typing import List, Literal
logging.basicConfig(level=logging.INFO)

ad_accounts = Blueprint('ad_accounts', __name__, url_prefix='/ad_accounts')
fb = Firebase(host=os.environ.get("FIREBASE_HOST"))
timezone = pytz.utc

class AdAccount(BaseModel):
    name: str
    account_id: str
    id: str
    user_id: str

class AdAccountCreation(BaseModel):
    property_id: str
    platform: Literal["facebook", "google_customer_match"]
    data: List[AdAccount]

@ad_accounts.route('', methods=["PUT", "GET"])
@ad_accounts.route('/', methods=["PUT", "GET"])
def ad_account_func():
    try:
        if request.method == 'PUT':
            data = request.get_json()
            try:
                validated = AdAccountCreation(**data)
            except ValidationError as e:
                return {"status": "error","message": "Invalid request payload","details": e.errors()}, 400
            
            property_id = data.get("property_id")
            objects = data.get("data")
            platform = data.get("platform")
            
            #check in property
            pack_acc = []
            for obj in objects:
                id = obj['id']
                ref = fb.db.reference(f"property/{property_id}/ad_accounts/{platform}")
                check = ref.child(id).get(shallow=True)
                obj['platform'] = platform
                if not check:
                    ref.child(id).set(obj)
                    ref.child(f"{id}/createdate").set(datetime.now(timezone).isoformat())
                    ref.child(f"{id}/lastupdate").set(datetime.now(timezone).isoformat())
                else:
                    ref.child(f"{id}/lastupdate").set(datetime.now(timezone).isoformat())
                
                pack_acc.append(id)
            
            return {'status': 'ok', 'message': f'All Ad account {", ".join(pack_acc)} has beed connected'}, 201

        elif request.method == 'GET':
            data = request.args
            for req in ['property_id']:
                if req not in data:
                    return {'message': f"{req} is required"}, 400
            
            property_id = data.get("property_id")
            platform = data.get("platform", 'all')
            ad_account_id = data.get("ad_account_id", 'all')

            if platform == 'all':
                accounts = ref.get()

            elif ad_account_id == 'all':
                accounts = ref.child(platform).get()

            else:
                # Fetches a specific account
                accounts = ref.child(f"{platform}/{ad_account_id}").get()
            
            result = accounts #not complete
            return {'status': 'ok', 'data': result}, 200

    
        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500

@ad_accounts.route('/facebook/generate-long-live-token', methods=['POST'])
@ad_accounts.route('/facebook/generate-long-live-token/', methods=['POST'])
def ad_account_facebook_generate_token_func():
    from utility.facebookAdSync import FacebookAdsSync
    try:
        if request.method == 'POST':
            data = request.get_json()
            check = Function.check_require_data(data, ['property_id', 'user_id', 'token'])
            if check != True:
                return check

            property_id = data.get("property_id")
            token = data.get("token")
            user_id = data.get("user_id")

            #check user in Firebase
            user = fb.db.reference(f"users/{user_id}").get(shallow=True)
            if not user:
                return {"status": "error", 'message': 'user not exist'}, 400
            #Set token
            fb_ad_sync = FacebookAdsSync(property_id, access_token=token)
            result = fb_ad_sync.generate_long_live_access_token(user_id)
            if result:
                return {'status': 'ok', "token": result}, 200
            return {'status': 'error', "message": "Try Again Later"}, 500

        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500

@ad_accounts.route('/custom_audience', methods=['POST', "GET", "PUT", "DELETE"])
@ad_accounts.route('/custom_audience/', methods=['POST', "GET", "PUT", "DELETE"])
def ad_account_custom_audience_func():
    class CustomAudienceData(BaseModel):
        audience_id: str
        ad_account: str
        method: Literal["DYNAMIC", "INCREMENTAL"]
    class PostAudienceRequest(BaseModel):
        property_id: str
        platform: Literal["facebook", "google_customer_match"]
        data: List[CustomAudienceData]
    
    class GetAudienceRequest(BaseModel):
        property_id: str
        platform: Literal["facebook", "google_customer_match", "all"]

    from utility.facebookAdSync import FacebookAdsSync
    try:
        if request.method == 'POST':
            data = request.get_json()
            try:
                validated = PostAudienceRequest(**data)
            except ValidationError as e:
                return {"status": "error","message": "Invalid request payload","details": e.errors()}, 400

            property_id = validated.property_id
            items = validated.data
            
            #Set create update
            final_items = []
            now = datetime.now().isoformat()
            base_ref = fb.db.reference(f"account/{property_id}/ad_audience_sync")

            ##Check Firebase
            for item in items:
                path = f"{item.audience_id}/{validated.platform}/{item.ad_account}/{item.method}"
                ref = base_ref.child(path)

                existing = ref.get()

                if existing:
                    existing["updatedate"] = now
                else:
                    ref.set({
                        "audience_id": item.audience_id,
                        "ad_account": item.ad_account,
                        "method": item.method,
                        "createdate": now,
                        "updatedate": now,
                        "status": "active"
                    })
            return {'status': 'ok', 'message': 'Link CDP audience to Facebook Custom Audience successfully',"added": len(final_items)}, 200
        elif request.method == 'GET':
            data = request.args
            try:
                validated = GetAudienceRequest(**data)
            except ValidationError as e:
                return {"status": "error","message": "Invalid request parameters","details": e.errors()}, 400

            ref = fb.db.reference(f"account/{validated.property_id}/ad_audience_sync")
            custom_audiences = ref.get() or {}
            if validated.platform != 'all':
                filtered = {}
                custom_audiences = ref.get() or {}
                for audience_id, platforms in custom_audiences.items():
                    if validated.platform in platforms:
                        filtered[audience_id] = {
                            validated.platform: platforms[validated.platform]
                        }
                return {'status': 'ok', 'data': filtered}, 200
            else:
                return {'status': 'ok', 'data': custom_audiences}, 200

        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': str(e)}, 500

@ad_accounts.route('/custom_audience/status', methods=['POST'])
@ad_accounts.route('/custom_audience/status/', methods=['POST'])
def ad_account_custom_audience_status_func():
    class PostCustomAudienceStatusData(BaseModel):
        audience_id: str
        platform: Literal["facebook", "google_customer_match"]
        ad_account: str
        status: Literal['active', 'inactive']
        method: Literal["DYNAMIC", "INCREMENTAL"]
    class PostCustomAudienceStatus(BaseModel):
        property_id: str
        data: List[PostCustomAudienceStatusData]
    try:
        if request.method == 'POST':
            request_data = request.get_json()
            try:
                validated = PostCustomAudienceStatus(**request_data)
            except ValidationError as e:
                return {"status": "error","message": "Invalid request parameters","details": e.errors()}, 400
            
            # Update status
            ref = fb.db.reference(f"account/{validated.property_id}/ad_audience_sync")
            request_data_json = validated.model_dump(exclude_none=True, mode='json')
            status_data = request_data_json['data']
            for item in status_data:
                ref.child(item['audience_id']).child(item['platform']).child(item['ad_account']).child(item['method']).child("status").set(item['status'])
            
            return {'status':'ok', 'message': 'Update Audience Sync status successfully', 'processed': len(status_data)}, 200
            
        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': str(e)}, 500

@ad_accounts.route('/facebook/custom-audiencce/list', methods=['GET'])
@ad_accounts.route('/facebook/custom-audiencce/list/', methods=['GET'])
def ad_account_facebook_list_custom_audience_func():
    class CustomAudienceRequest(BaseModel):
        property_id: str
        ad_account_id: str
    from utility.facebookAdSync import FacebookAdsSync
    try:
        if request.method == 'GET':
            data = request.args
            try:
                validated = CustomAudienceRequest(**data)
            except ValidationError as e:
                return {"status": "error","message": "Invalid request payload","details": e.errors()}, 400

            property_id = data.get("property_id")
            ad_account_id = data.get("ad_account_id")
            
            #Set token
            fb_ad_sync = FacebookAdsSync(property_id=property_id)
            result = fb_ad_sync.list_custom_audiences(ad_account_id)
            if result:
                return {'status': 'ok', "data": result}, 200
            return {'status': 'error', "message": "Try Again Later"}, 500

        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500
    
@ad_accounts.route('/facebook/custom-audiencce/execute', methods=['GET'])
@ad_accounts.route('/facebook/custom-audiencce/execute/', methods=['GET'])
def ad_account_facebook_custom_audience_execute_func():
    from utility.facebookAdSync import FacebookAdsSync
    LOG_PREFIX = "FacebookAdsSync Auto:"
    try:
        success = []
        properties = fb.db.reference("account").get(shallow=True)
        for property_id in properties:
            fbsync = FacebookAdsSync(property_id=property_id)
            logs = fbsync.mange_execution()
            if logs:
                success.append(property_id)
        
        return {'status': 'ok', 'message': f'{LOG_PREFIX} success properties | {",".join(success)}'}, 200

    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500


@ad_accounts.route('/google_customer_match/generate_refresh_token', methods=['POST'])
@ad_accounts.route('/google_customer_match/generate_refresh_token/', methods=['POST'])
def ad_account_google_customer_match_generate_refresh_token_func():
    from utility.googleCustomerMatch import GoogleCustomerMatch
    try:
        if request.method == 'POST':
            data = request.get_json()
            check = Function.check_require_data(data, ['property_id', "user_id", 'code'])
            if check != True:
                return check

            property_id = data.get("property_id")
            code = data.get("code")
            user_id = data.get("user_id")
            gcm = GoogleCustomerMatch()
            response = gcm.fetch_refresh_token(code=code)
            if response['status'] == 'error':
                return {'status': 'error', 'message': 'Invalid code'}, 400

            #Save token to user
            fb.db.reference(f"users/{user_id}/ad_account/google_customer_match/").set(response)
            return {'status': 'ok'}, 200
            
        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500

# @ad_accounts.route('/google_customer_match/mcc', methods=['GET'])
# @ad_accounts.route('/google_customer_match/mcc/', methods=['GET'])
# def ad_account_google_customer_match_mcc_func():
#     from utility.googleCustomerMatch import GoogleCustomerMatch
#     try:
#         if request.method == 'GET':
#             data = request.args
#             check = Function.check_require_data(data, ['property_id', "user_id"])
#             if check != True:
#                 return check

#             property_id = data.get("property_id")
#             user_id = data.get("user_id")
#             #Get access_token
#             gcm = GoogleCustomerMatch()
#             access_token = gcm.get_access_token(user_id)
#             if not access_token:
#                 return {'status': 'error', 'message': "User dosen't login to the system"}, 400
#             accounts = gcm.list_accounts()
#             return {'status': 'ok', 'data': accounts}, 200
            
#         return {'status': 'error', 'message': 'Method not allowed'}, 405
#     except Exception as e:
#         logging.error(e)
#         return {'status': 'error', 'message': e}, 500
    
@ad_accounts.route('/google_customer_match/ad_account', methods=['GET'])
@ad_accounts.route('/google_customer_match/ad_account/', methods=['GET'])
def ad_account_google_customer_match_ad_account_func():
    from utility.googleCustomerMatch import GoogleCustomerMatch
    try:
        if request.method == 'GET':
            data = request.args
            check = Function.check_require_data(data, ['property_id', "user_id"])
            if check != True:
                return check

            property_id = data.get("property_id")
            user_id = data.get("user_id")
            #Get access_token
            gcm = GoogleCustomerMatch()
            access_token = gcm.get_access_token(user_id)
            if not access_token:
                return {'status': 'error', 'message': "User dosen't login to the system"}, 400
            accounts = gcm.search_client_customers()
            return {'status': 'ok', 'data': accounts}, 200
            
        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500

@ad_accounts.route('/google_customer_match/custom_audience', methods=['POST', "GET", "PUT", "DELETE"])
@ad_accounts.route('/google_customer_match/custom_audience/', methods=['POST', "GET", "PUT", "DELETE"])
def ad_account_google_customer_match_custom_audience_func():
    try:
        if request.method == 'POST':
            data = request.get_json()
            check = Function.check_require_data(data, ['property_id', 'audience_ids', 'ad_accounts', 'method'])
            if check != True:
                return check
            method = data.get("method")
            if method not in ['CREATE']:
                return {'status': 'error', 'message': """method must be 'CREATE'"""}, 400

            property_id = data.get("property_id")
            ad_accounts = data.get("ad_accounts")
            audience_ids = data.get("audience_ids")
            
            if type(ad_accounts) != list or type(audience_ids) != list:
                return {'status': 'error', 'message': 'custom_audience_ids and audience_ids must be array'}, 400

            pack = {au: list(ad_accounts) for au in audience_ids}

            ref = fb.db.reference(f"account/{property_id}/ad_audience_sync/google_customer_match")

            ##Check Firebase
            check = ref.get()

            ##Check and de-duplicate process
            if check:
                for au, new_accounts in pack.items():
                    existing_accounts = check.get(au, [])
                    unique_accounts = list(set(existing_accounts) | set(new_accounts))
                    check[au] = unique_accounts
                
                pack = check
            ## Save
            ref.set(pack)
            return {'status': 'ok', 'message': 'Link CDP audience to Google Customer Match Custom Audience successfully'}, 200
        return {'status': 'error', 'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500