from flask import Blueprint, request, jsonify
import logging
import pytz
import os
import json
import uuid
from datetime import datetime
from utility.media import Facebook
from utility.line import OA, Audience
from utility.function import Function
from firebase.firebase import Firebase
from utility.authorization import CloudAuthorization as CAuth
from utility.authorization import TokenAuthorization
logging.basicConfig(level=logging.INFO)

channel = Blueprint('channel', __name__, url_prefix='/channel')
fb = Firebase(host=os.environ.get("FIREBASE_HOST"))

@channel.route('/facebook/page/', methods=['POST','GET', 'PUT','DELETE'])
def channel_fb_page():
    timezone = pytz.timezone('Asia/Bangkok')
    now = datetime.now(timezone)
    time = now.strftime('%Y-%m-%d %H:%M:%S')
    
    local_key = request.headers.get('Authorization')
    bearer = local_key.replace("Bearer ", "")
    userAuth = TokenAuthorization(access_token=bearer)
    userData = userAuth.validate()

    if request.method == 'GET':
        pages = Facebook.get_all_page()
        
        page_list = []
        for page in pages:
            page_detail = {
                "access_token" : page['access_token'],
                "page_id" : page['id'],
                "page_name" : page['name']
            }
            page_list.append(page_detail)
            
        return {'message': 'Success', 'data': page_list}, 200
    
    return {"status":"error",'message': 'Method not allowed'}, 405


@channel.route('/facebook/page/subscription/', methods=['POST','GET', 'PUT','DELETE'])
def channel_fb_page_subscription():
    timezone = pytz.timezone('Asia/Bangkok')
    now = datetime.now(timezone)
    time = now.strftime('%Y-%m-%d %H:%M:%S')
    
    local_key = request.headers.get('Authorization')
    bearer = local_key.replace("Bearer ", "")
    userAuth = TokenAuthorization(access_token=bearer)
    userData = userAuth.validate()
    userPropertyList = userData.get('property', {})
    

    if request.method == 'GET':
        
        data = request.args
        property_id = data.get("property_id", None)
        page_id = data.get("page_id", None)
        
        if not page_id and not property_id:
            return {"status":"error",'message': "page_id and property_id is required"}, 400
        
        fb_channels = fb.db.reference(f'property/{property_id}/channel/facebook/{page_id}').get()
        
        # if fb_channels:
        #     for channel in fb_channels:
        #         if str(page_id) in channel:
        #             page_info = channel[str(page_id)]
        #             page_info['page_id'] = page_id

        if fb_channels:
            return {'message': 'Success', 'data': fb_channels}, 200
        else:
            return {"status":"error",'message': "Page ID not found in this property or Page ID is not accessible for this property"}, 400
        
    elif request.method == 'POST':
        data = request.get_json()
        logging.info(data)
        logging.info(f"Property data: {userPropertyList}")
        
        for req in ['property_id','page_id','page_access_token','subscribed_fields']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require for adding subscribed fields"}, 400
            
        property_id = data.get("property_id", None)
        page_id = data.get("page_id", None)
        page_access_token = data.get("page_access_token", None)
        subscribed_fields = data.get("subscribed_fields", [])
        
        logging.info(f"Property data: {userPropertyList}, Property ID: {property_id}")
        if str(property_id) not in userPropertyList:
            return {"status":"error",'message': f"You don't have access to this property {property_id}"}, 400
        
        page_obj = Facebook.get_page_obj(page_access_token,page_id)
        page_obj.create_subscribed_app(params={"subscribed_fields":subscribed_fields})
        page_detail = page_obj.get_subscribed_apps()
        
        json_data = {
            page_id : {
                "createdate" : datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "lastupdate" : datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "name" : page_obj.api_get(fields=['name'])['name'],
                "category" : page_detail[0]['category'],
                "link" : page_detail[0]['link'],
                "app_name" : page_detail[0]['name'],
                "subscribed_fields" : page_detail[0]["subscribed_fields"]
            }
        }
        ref = fb.db.reference(f'property/{property_id}/channel/facebook')
        current_fb_index = ref.get()
        
        if current_fb_index and str(page_id) in current_fb_index:
            # Only update 'lastupdate' and 'subscribed_fields'
            ref.child(str(page_id)).update({
                "lastupdate": datetime.now(timezone).strftime("%Y-%m-%d %H:%M:%S"),
                "subscribed_fields": page_detail[0]["subscribed_fields"]
            })
        else:
            # Create new entry for this page_id
            ref.child(str(page_id)).set(json_data[page_id])
        
        fb.db.reference(f'property/{property_id}/lastupdate').set(datetime.now(timezone).strftime("%Y-%m-%d %H:%M:%S"))
        
        return {"status":"success",'message': 'Subscribed fields successfully','data':json_data}, 200
    
    return {"status":"error",'message': 'Method not allowed'}, 405

@channel.route('/facebook/page/unsubscription/', methods=['POST','GET', 'PUT','DELETE'])
def channel_fb_page_unsubscription():
    
    timezone = pytz.timezone('Asia/Bangkok')
    now = datetime.now(timezone)
    time = now.strftime('%Y-%m-%d %H:%M:%S')
    
    local_key = request.headers.get('Authorization')
    bearer = local_key.replace("Bearer ", "")
    userAuth = TokenAuthorization(access_token=bearer)
    userData = userAuth.validate()
    userPropertyList = userData.get('property', {})
    
    if request.method == 'POST':
        data = request.get_json()
        
        for req in ['property_id','page_id','page_access_token']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require for adding subscribed fields"}, 400
            
        property_id = data.get("property_id", None)
        page_id = data.get("page_id", None)
        page_access_token = data.get("page_access_token", None)
        
        if property_id not in userPropertyList:
            return {"status":"error",'message': f"You don't have access to this property"}, 400
        
        page_obj = Facebook.get_page_obj(page_access_token,page_id)
        page_obj.create_subscribed_app(params={"subscribed_fields":[]})
        
        ref = fb.db.reference(f'property/{property_id}/channel/facebook')
        current_fb_index = ref.get()

        if current_fb_index and str(page_id) in current_fb_index:
            # Delete the entire page entry directly using page_id
            ref.child(str(page_id)).delete()
        
        return {"status":"success",'message': 'Unsubscribed fields successfully'}, 200
    
    return {"status":"error",'message': 'Method not allowed'}, 405

@channel.route('/line/page/subscription/', methods=['POST'])
@channel.route('/line/page/subscription', methods=['POST'])
def channel_line_page_subscription():
    
    timezone = pytz.timezone('Asia/Bangkok')
    now = datetime.now(timezone)
    time = now.strftime('%Y-%m-%d %H:%M:%S')
    
    user_id = Function.getUserID(request)
    
    local_key = request.headers.get('Authorization')
    bearer = local_key.replace("Bearer ", "")

    userAuth = TokenAuthorization(access_token=bearer)
    userData = userAuth.validate()
    
    if request.method == 'POST':
        data = request.get_json()
        
        for req in ['property_id','access_token']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require for LINE channel added"}, 400
        
        property_id = data.get("property_id")
        access_token = data.get("access_token")
        
            
        if str(property_id) not in list(userData['property']):
            return {"status":"error",'message': 'This property is not belong to you'}, 400
        
        # Gen ID
        id = Function.text_to_numeric_token(access_token)
        channelEx = fb.db.reference().child(f"property/{property_id}/channel/line/{id}").get(shallow=True)
        
        if channelEx:
            return {'status': 'error', 'message': 'This channel already created'},400
        
        #Get line OA profile
        oa = OA(access_token=access_token)
        respone = oa.getOAProfile()
        respone_data = respone.json()

        if respone.status_code != 200:
            return {"status":"error",'message': "LINE Access token was wrong"},400

        displayname = respone_data['displayName']
        picture_url = respone_data.get('pictureUrl', None)
        LINE_userId = respone_data['userId']

        line_context = {
            'userId': LINE_userId,
            'name': displayname,
            'picture': picture_url,
            'createdate': time,
            'lastupdate': time
        }

        #Update env
        cauth = CAuth()
        access_token = cauth.get_access_token()
        info = cauth.get_cloud_run_service_info(access_token=access_token)
        actk_obj = [{
            "name": f"LINE_{id}",
            "value": data.get('access_token')
        }]

        response_add = cauth.add_cloud_run_service_env_var(info=info, env_pack=actk_obj, access_token=access_token)
        
        if response_add.status_code != 200:
            return {"status":"error",'message': 'update env var failed'}, 500

        info = cauth.get_cloud_run_service_info(access_token=access_token)
        env_updates = {
            "LINE_CHANNEL_LIST" : LINE_userId
        }
        response_update = cauth.update_cloud_run_service_env_var(access_token=access_token, info=info, env_updates=env_updates)

        if response_update.status_code != 200:
            return {"status":"error",'message': 'update env var failed'}, 500

        job = fb.db.reference().child(f"property/{property_id}/channel/line/{id}").set(line_context)

        # #Flow pull exsit audience
        # au = Audience(access_token=data.get('access_token'))
        # audience_list = au.get_list_audience_data()
        # if audience_list:
        #     job_audience = fb.add_line_exsit_audiece(property_id=property_id, channel_id=id, data=audience_list)
            
        #Set user data
        job = fb.db.reference().child(f"""users/{user_id}/property/{property_id}/channel/{id}/role""").set("owner")
            
        #Add channel to master
        masterUser = fb.get_by_child(child=f"ydm_messaging_tool/users/master")
        if masterUser:
            listUserMaster = list(masterUser)
            for user in listUserMaster:
                try:
                    jobMaster = fb.db.reference().child(f"""users/{user}/property/{property_id}/channel/{id}/role""").update("master")
                except:
                    jobMaster = fb.db.reference().child(f"""users/{user}/property/{property_id}/channel/{id}/role""").set("master")

        #Check LINE hook sub
        hookCode, hookC0ntent = oa.getHookEndpoint()
        json_hookC0ntent = json.loads(hookC0ntent)
        
        #Gen webhook path
        endpoint = f"https://ydm-profile-360-907324191702.asia-southeast1.run.app/hook/line/{id}"
        if hookCode == 404:
            setHookCode, setHookC0ntent = oa.putHookEndpoint(endpoint=endpoint)
            if setHookCode == 200:
                message = f"""System integrated successfully!\nNext step, enable webhook on LINE official account console to receive data from your LINE Official Account, please contact your administrator"""
                return {"status":"ok",'message': message, "hook": endpoint}, 200
            return {"status":"error",'message': f'Fail in add webhook to channel {id}\n\n{setHookC0ntent}'}, 500
        else:
            #Message to user
            message = f"""Your LINE channel is already subscribed to a webhook.
To allow this system to receive data from your LINE Official Account, please contact your administrator to update the webhook endpoint."""
            
            return {"status":"ok",'message': message, "hook": endpoint, "existing_hook": json_hookC0ntent['endpoint']}, 200

@channel.route('/ga4', methods=['GET'])
@channel.route('/ga4/', methods=['GET'])
def channel_ga4():
    try:
        if request.method == 'GET':
            data = request.args
            for req in ['property_id','ga4_property_id']:
                if req not in data:
                    return {"status":"error",'message': f"{req} is require"}, 400
                
            property_id = data.get("property_id")
            ga4_property_id = data.get("ga4_property_id", 'all')

            if ga4_property_id == 'all':
                data = []
                context = fb.db.reference().child(f"property/{property_id}/channel/ga4").get()
                if context:
                    data = [context[i] for i in context]
            else:
                data = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}").get()
            
            return {'status': 'ok', 'data': data}, 200
        
        return {"status":"error",'message': 'Method not allowed'}, 405
    
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e},500

@channel.route('/ga4/property/subscription/', methods=['POST', 'DELETE'])
@channel.route('/ga4/property/subscription', methods=['POST','DELETE'])
def channel_ga4_property_subscription():
    timezone = pytz.timezone('Asia/Bangkok')
    now = datetime.now(timezone)
    time = now.strftime('%Y-%m-%d %H:%M:%S')
    if request.method == 'POST':
        data = request.get_json()
        
        for req in ['property_id','ga4_property_id']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require for GA4 property subscription"}, 400
        
        property_id = data.get("property_id")
        ga4_property_id = data.get("ga4_property_id")

        job = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/id").set(ga4_property_id)
        #update
        jobUpdate = fb.db.reference().child(f"property/{property_id}/lastupdate").set(time)

        #Link bigquery
        from ga4.ga4 import GA4
        try:
            #Check linked
            ga4client = GA4(property_id=ga4_property_id)
            check = ga4client.check_linked_ga4()
            if check == False:
                isLinked = ga4client.link_bigquery()
                return {"status":"success",'message': 'Subscribed GA4 property successfully and Bigquery Linked'}, 200 if isLinked else {"status":"success",'message': 'Subscribed GA4 property successfully and Bigquery Linked'}, 200
            else:
                return {"status":"success", "message": f'Subscribed GA4 property successfully but this property already linked to Bigquery project {check}'}, 200
        except Exception as e:
            return {"status":"success",'message': f'Subscribed GA4 property successfully but Bigquery link error {e}'}, 200
    
    elif request.method == 'DELETE':
        for req in ['property_id','ga4_property_id']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require for GA4 property subscription"}, 400
        
        property_id = data.get("property_id")
        ga4_property_id = data.get("ga4_property_id")

        job = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}").delete()
        #update
        jobUpdate = fb.db.reference().child(f"property/{property_id}/lastupdate").set(time)
        return {"status":"success",'message': 'Unsubscribed GA4 property successfully'}, 200

    return {"status":"error",'message': 'Method not allowed'}, 405

@channel.route('/ga4/property/userproperty/list', methods=['GET'])
@channel.route('/ga4/property/userproperty/list/', methods=['GET'])
def channel_ga4_get_user_property_list():
    try:
        if request.method == 'GET':
            data = request.args
            for req in ['property_id','ga4_property_id']:
                if req not in data:
                    return {"status":"error",'message': f"{req} is require"}, 400
                
            property_id = data.get("property_id")
            ga4_property_id = data.get("ga4_property_id")
            
            unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").get()
            used_property_name = []
            if unifiedDimension:
                for i in unifiedDimension:
                    name = i['name']
                    used_property_name.append(name)

            #Get GA4 property
            from ga4.ga4 import GA4
            ga4Client = GA4(property_id=str(ga4_property_id))
            data = ga4Client.get_user_property_metadata('User-scoped Custom Dimension')
            
            for i in data:
                i['used'] = True if i['user_property_name'] in used_property_name else False
            
            return {'status': 'success', 'data': data}, 200
        
        return {"status":"error",'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': f'Error {e}'}

@channel.route('/ga4/property/userproperty/', methods=['GET', 'POST', 'PUT'])
@channel.route('/ga4/property/userproperty', methods=['GET', "POST", 'PUT'])
def channel_ga4_get_user_property():
    if request.method == 'GET':
        data = request.args
        for req in ['property_id','ga4_property_id']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require"}, 400
    
        property_id = data.get("property_id")
        ga4_property_id = data.get("ga4_property_id")

        # #Get GA4 property
        # from ga4.ga4 import GA4
        # ga4Client = GA4(property_id=str(ga4_property_id))
        # data = ga4Client.get_user_property_metadata('User-scoped Custom Dimension')

        # job = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/list").set(data)
        unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").get()
        # listDimensionUnified = []
        # if unifiedDimension:
        #     for i in unifiedDimension:
        #         value = unifiedDimension[i]
        #         listDimensionUnified.append(value)

        # for i in data:
        #     i['is_unify'] = True if i['user_property_name'] in listDimensionUnified else False

        return {'status': 'success','data': unifiedDimension},200
    
    elif request.method == 'POST':
        data = request.get_json()
        for req in ['property_id','ga4_property_id', 'unify']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require"}, 400
        
        if type(data['unify']) != list:
            return {"status":"error",'message': f"Type of unify must be array"}, 400
        property_id = data.get("property_id")
        ga4_property_id = data.get("ga4_property_id")
        unify_dict = data.get("unify")
        
        for u in unify_dict:
            ref_user_property = u['ref_user_property']
            if ref_user_property not in ['phoneNumber', 'facebook', 'line', 'email']:
                return {'status': 'error', 'message': f'{ref_user_property} is not valid key for GA4 setup filed for unify process'}, 400
        unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").get()
        pack_unify = [] if not unifiedDimension else unifiedDimension
        for u in unify_dict:
            pack = {
                "id": str(uuid.uuid4()),
                "name": u['name'],
                "ref_user_property": u['ref_user_property'],
                "is_hashed": bool(u['is_hashed']),
                "active": False
            }
            pack_unify.append(pack)
        
        unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").set(pack_unify)

        return {"status":"success",'message': 'Update GA4 unify dimension successfully', 'data': pack_unify}, 200
    
    elif request.method == 'PUT':
        data = request.get_json()
        for req in ['property_id','ga4_property_id', 'id', 'data']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require"}, 400
        # if type(data['unify']) != list:
        #     return {"status":"error",'message': f"Type of unify must be array"}, 400
        property_id = data.get("property_id")
        ga4_property_id = data.get("ga4_property_id")
        id = data.get("id")
        updated_data = data.get('data')

        unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").get()
        fond = False
        for i in range(len(unifiedDimension)):
            if unifiedDimension[i]['id'] == id:
                fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified/{i}").set(updated_data)
                fond = True
            break

        if fond:
            return {'status': 'success', 'message': f'Update GA4 unify dimension ({id}) successfully'}, 200
        else:
            return {'status': 'not_fond', 'message': f'GA4 unify dimension ({id}) not fond'}, 404

    return {"status":"error",'message': 'Method not allowed'}, 405

@channel.route('/ga4/property/userproperty/status', methods=['POST'])
@channel.route('/ga4/property/userproperty/status/', methods=['POST'])
def channel_ga4_get_user_property_status():
    try:
        if request.method == 'POST':
            data = request.get_json()
            for req in ['property_id','ga4_property_id', 'id', 'active']:
                if req not in data:
                    return {"status":"error",'message': f"{req} is require"}, 400
            property_id = data.get("property_id")
            ga4_property_id = data.get("ga4_property_id")
            id = data.get("id")
            active = data.get('active')
            active = bool(active)
            unifiedDimension = fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified").get()
            fond = False
            for i in range(len(unifiedDimension)):
                if unifiedDimension[i]['id'] == id:
                    fb.db.reference().child(f"property/{property_id}/channel/ga4/{ga4_property_id}/userProperty/unified/{i}/active").set(active)
                    fond = True
                break

            if fond:
                return {'status': 'success', 'message': f'Update status GA4 unify dimension ({id}) successfully'}, 200
            else:
                return {'status': 'not_fond', 'message': f'GA4 unify dimension ({id}) not fond'}, 404
        
        return {"status":"error",'message': 'Method not allowed'}, 405
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': f'Error {e}'}


@channel.route('/line/webhook-management/enable/', methods=['POST'])
@channel.route('/line/webhook-management/enable', methods=['POST'])
def line_webhook_management_enable():
    try:
        data = request.get_json()
        for req in ['property_id','channel_id']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require"}, 400
        
        property_id = data.get("property_id")
        channel_id = data.get("channel_id")

        topic_id = f"line_webhook_{property_id}_{channel_id}"

        from google.cloud import pubsub_v1
        publisher = pubsub_v1.PublisherClient()
        topic_path = publisher.topic_path(os.environ.get("GCP_PROJECT"), topic_id)

        topic = publisher.create_topic(request={"name": topic_path})
        if topic:
            return {'status': 'ok', 'message': f'Enable LINE webhook management feature for channel {channel_id} successfully'}, 200
        
        return {'status': 'pending', 'message': 'Please try again'}, 400
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500

@channel.route('/line/webhook-management/add/', methods=['POST'])
@channel.route('/line/webhook-management/add', methods=['POST'])
def line_webhook_management_add():
    try:
        from google.cloud import pubsub_v1
        
        data = request.get_json()
        for req in ['property_id','channel_id', 'webhooks']:
            if req not in data:
                return {"status":"error",'message': f"{req} is require"}, 400
        
        property_id = data.get("property_id")
        channel_id = data.get("channel_id")
        webhooks = data.get("webhooks")
        
        if type(webhooks) != list:
            return {"status":"error",'message': f"webhooks parameter must be array"}, 400
        
        topic_id = f"line_webhook_{property_id}_{channel_id}"
        

        publisher = pubsub_v1.PublisherClient()
        subscriber = pubsub_v1.SubscriberClient()
        topic_path = publisher.topic_path(os.environ.get("GCP_PROJECT"), topic_id)
        
        for web in webhooks:
            subscription_id = str(uuid.uuid4())
            subscription_path = subscriber.subscription_path(os.environ.get("GCP_PROJECT"), subscription_id)
        
            push_config = pubsub_v1.types.PushConfig(push_endpoint=web)
            with subscriber:
                subscription = subscriber.create_subscription(
                    request={
                        "name": subscription_path,
                        "topic": topic_path,
                        "push_config": push_config,
                    }
                )

            logging.info(f"Push subscription created: {subscription}.")
        
        return {'status': 'ok', 'message': 'New webhook added to webhook managment system successfully'}, 200
    
    except Exception as e:
        logging.error(e)
        return {'status': 'error', 'message': e}, 500