from src.marketing.apps.post.model import CalendarPost, CalendarPostType
from src.marketing.apps.Calendar.model import Calendar
from sqlalchemy.orm import Session
from src.marketing.apps.post import schema
from fastapi import HTTPException,status
from typing import List, Optional
from pytz import timezone, UTC,AmbiguousTimeError, NonExistentTimeError
from datetime import datetime, time
from .model import PostImage
import logging 
from pytz.exceptions import UnknownTimeZoneError
from src.marketing.apps.post.schema import GeneratedImage,GenerateImageContent,GenerateTextContent, GeneratedImageResponse,GeneratedTextResponse
from src.utils.settings import settings
from .schema import URLInfoResponse
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse
from .model import PostImage
import os
from openai import AzureOpenAI
from .utils import convert_to_utc, post_to_twitter, post_to_facebook
# from dotenv import dotenv_values
from dotenv import load_dotenv
load_dotenv()


logging.basicConfig(level=logging.INFO)


import uuid
import shutil
BASE_URL = os.getenv("BASE_URL", "http://localhost:8000")
# BASE_URL = "http://localhost:8000"

# Adjust path
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
STATIC_PATH = os.path.join(BASE_DIR, "menu_design", "designer", "static", "marketing", "images")

os.makedirs(STATIC_PATH, exist_ok=True)



# #create a new calendar post with timezone convert
#create a new calendar post with timezone convert
# def create_calendar_post(db: Session, post_data: schema.CalendarPostCreate) -> schema.CalendarPostOut:
#     # Fetch the associated calendar to get its timezone
#     calendar = db.query(Calendar).filter(Calendar.id == post_data.calendar_id).first()
#     if not calendar:
#         raise HTTPException(status_code=404, detail="Calendar not found")

#     # Use the timezone from calendar, default to UTC if missing
#     # user_tz = timezone(calendar.timezone or "UTC")
#     try:
#         user_tz = timezone((calendar.timezone or "UTC").strip())
#     except UnknownTimeZoneError:
#         raise HTTPException(status_code=400, detail=f"Invalid timezone: {calendar.timezone}")

#     # # Combine the local date and time
#     local_datetime = datetime.combine(post_data.schedule_date, post_data.schedule_time or time(0, 0))

#     # # Localize and convert to UTC
#     # local_dt_with_tz = user_tz.localize(local_datetime)
#     # utc_datetime = local_dt_with_tz.astimezone(UTC)
#     # Localize with DST handling
#     try:
#         local_dt_with_tz = user_tz.localize(local_datetime, is_dst=None)  # Let pytz figure out DST
#     except AmbiguousTimeError:
#         # If the time is ambiguous (e.g., during the fall DST switch), choose DST=False by default
#         local_dt_with_tz = user_tz.localize(local_datetime, is_dst=False)
#     except NonExistentTimeError:
#         # If the time does not exist (e.g., during the spring DST switch), reject or shift it
#         raise HTTPException(status_code=400, detail="The scheduled time does not exist due to daylight saving time transition.")
#     utc_datetime = local_dt_with_tz.astimezone(UTC)
#     #DEBUG CHECK
#     logging.info(f"[DEBUG] Calendar Timezone: {user_tz}")
#     logging.info(f"[DEBUG] Local Datetime ({user_tz}): {local_datetime}")
#     logging.info(f"[DEBUG] UTC Converted Datetime: {utc_datetime}")

#     # Replace input with UTC values
#     post_data.schedule_date = utc_datetime.date()
#     post_data.schedule_time = utc_datetime.time()
    

#     # Create the post with UTC timestamp
#     post = CalendarPost(**post_data.model_dump())
#     db.add(post)
#     db.commit()
#     db.refresh(post)
#     return schema.CalendarPostOut.model_validate(post)

def create_calendar_post(db: Session, post_data: schema.CalendarPostCreate) -> schema.CalendarPostOut:
    if post_data.is_instant_post:
        post_data.calendar_id = None
        now_utc = datetime.utcnow()
        post_data.schedule_date = now_utc.date()
        post_data.schedule_time = now_utc.time()
        logging.info(f"[InstantPost] Using current UTC schedule: {post_data.schedule_date} {post_data.schedule_time}")
    else:
        calendar = db.query(Calendar).filter(Calendar.id == post_data.calendar_id).first()
        if not calendar:
            raise HTTPException(status_code=404, detail="Calendar not found")
        try:
            user_tz = timezone((calendar.timezone or "UTC").strip())
        except UnknownTimeZoneError:
            raise HTTPException(status_code=400, detail=f"Invalid timezone: {calendar.timezone}")
        local_datetime = datetime.combine(
            post_data.schedule_date,
            post_data.schedule_time or time(0, 0)
        )
        try:
            local_dt_with_tz = user_tz.localize(local_datetime, is_dst=None)
        except AmbiguousTimeError:
            local_dt_with_tz = user_tz.localize(localDatetime, is_dst=False)
        except NonExistentTimeError:
            raise HTTPException(status_code=400, detail="The scheduled time does not exist due to DST transition.")
        utc_datetime = local_dt_with_tz.astimezone(UTC)
        post_data.schedule_date = utc_datetime.date()
        post_data.schedule_time = utc_datetime.time()

    try:
        # Only insert valid DB fields
        valid_fields = {c.name for c in CalendarPost.__table__.columns}
        post_dict = {k: v for k, v in post_data.model_dump().items() if k in valid_fields}
        post = CalendarPost(**post_dict)

        db.add(post)
        db.commit()
        db.refresh(post)
    except Exception as e:
        import traceback
        logging.error(f"[CreateCalendarPost] Error creating post: {e}")
        logging.error(traceback.format_exc())
        db.rollback()
        raise HTTPException(status_code=500, detail="Error creating post")

    return schema.CalendarPostOut.model_validate(post)






# Get a calendar post by ID
def get_calendar_post_by_id(db: Session, post_id: int) -> Optional[schema.CalendarPostOut]:
    post = db.query(CalendarPost).filter(
        CalendarPost.id == post_id,
        CalendarPost.is_deleted==False,
        CalendarPost.post_status != "archived" #This Line added in 28-07-2025
        ).first()
    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post not found")
    return schema.CalendarPostOut.model_validate(post)



#Get Data  calendar post by branch_id and schedule_date
def get_calendar_posts_by_calendar_id_and_date_range(
    db: Session,
    calendar_id: int,
    start_date: str,
    end_date: str
) -> List[schema.CalendarPostOut]:
    posts = db.query(CalendarPost).filter(
        CalendarPost.calendar_id == calendar_id,
        CalendarPost.schedule_date >= start_date,
        CalendarPost.schedule_date <= end_date,
        CalendarPost.is_deleted == False
    ).all()
    
    # If no posts found, return None
    if not posts:
        return None
    
    return [schema.CalendarPostOut.model_validate(post) for post in posts]
   


# Update a calendar post
# def update_calendar_post(db: Session, post_id: int, post_data: schema.CalendarPostUpdate) -> schema.CalendarPostOut:
#     post = db.query(CalendarPost).filter(CalendarPost.id == post_id,CalendarPost.is_deleted==False).first()
#     if not post:
#         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post not found")
    
#     #Update 28-07-20205 Start
#     allowed_statuses = {"draft","submitted", "published", "archived", "paused"}
#     if post_data.post_status and post_data.post_status not in allowed_statuses:
#         raise HTTPException(status_code=400, detail="Invalid post status")
#     ##Update 28-07-20205 End
#     #----------------------------Time Date convert to utc -------------------------
#     if post_data.schedule_date is not None or post_data.schedule_time is not None:
#         calendar_id = post.calendar_id
#         calendar = db.query(Calendar).filter(Calendar.id == calendar_id).first()
#         if not calendar:
#             raise HTTPException(status_code=404, detail="Calendar not found")
#         utc_datetime = convert_to_utc(post_data.schedule_date, post_data.schedule_time, calendar.timezone or "UTC")
#         post_data.schedule_date = utc_datetime.date()
#         post_data.schedule_time = utc_datetime.time()

#     #----------------------------Time Date convert to utc -------------------------
    
#     for key, value in post_data.model_dump(exclude_unset=True).items():
#         setattr(post, key, value)
    
#     db.commit()
#     db.refresh(post)
#     return schema.CalendarPostOut.model_validate(post)

# Update a calendar post
def update_calendar_post(db: Session, post_id: int, post_data: schema.CalendarPostUpdate) -> schema.CalendarPostOut:
    post = db.query(CalendarPost).filter(
        CalendarPost.id == post_id,
        CalendarPost.is_deleted == False
    ).first()

    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post not found")
    
    # ----------------- Validate post_status -----------------
    allowed_statuses = {"draft", "submitted", "published", "archived", "paused"}
    if post_data.post_status and post_data.post_status not in allowed_statuses:
        raise HTTPException(status_code=400, detail="Invalid post status")
    
    # ----------------- Handle is_instant_post -----------------
    if post_data.is_instant_post:
        # Instant post → ignore calendar_id and use current UTC
        post_data.calendar_id = None
        now_utc = datetime.utcnow()
        post_data.schedule_date = now_utc.date()
        post_data.schedule_time = now_utc.time()
        logging.info(f"[InstantPost][UPDATE] Using current UTC schedule: {post_data.schedule_date} {post_data.schedule_time}")

    # ----------------- Else: Convert to UTC -----------------
    elif post_data.schedule_date is not None or post_data.schedule_time is not None:
        calendar_id = post_data.calendar_id or post.calendar_id  # prefer update payload, else fallback
        calendar = db.query(Calendar).filter(Calendar.id == calendar_id).first()
        if not calendar:
            raise HTTPException(status_code=404, detail="Calendar not found")

        utc_datetime = convert_to_utc(
            post_data.schedule_date or post.schedule_date,
            post_data.schedule_time or post.schedule_time,
            calendar.timezone or "UTC"
        )
        post_data.schedule_date = utc_datetime.date()
        post_data.schedule_time = utc_datetime.time()
        logging.info(f"[Update] Converted to UTC: {utc_datetime}")

    # ----------------- Apply changes -----------------
    for key, value in post_data.model_dump(exclude_unset=True).items():
        setattr(post, key, value)
    
    db.commit()
    db.refresh(post)
    return schema.CalendarPostOut.model_validate(post)


#partial_update
def partial_update_calendar_post(post_id: int, post_data: schema.CalendarPostUpdate, db: Session) -> schema.CalendarPostOut:
    post = db.query(CalendarPost).filter(CalendarPost.id == post_id, CalendarPost.is_deleted == False).first()
    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Post not found")
    
    # Apply only provided fields
    for key, value in post_data.model_dump(exclude_unset=True).items():
        setattr(post, key, value)

    db.commit()
    db.refresh(post)
    return schema.CalendarPostOut.model_validate(post)


# Delete a calendar post
def delete_calendar_post(db: Session, post_id: int):
    post = db.query(CalendarPost).filter(CalendarPost.id == post_id).first()
    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post not found")
    

    post.is_deleted = True
    db.commit()


#If not archive then list all of things 
def list_calendar_posts(db: Session) -> List[schema.CalendarPostOut]:
    posts = db.query(CalendarPost).filter(
        CalendarPost.post_status != "archived",
        CalendarPost.is_deleted == False
    ).all()
    return [schema.CalendarPostOut.model_validate(p) for p in posts]

#--------------------------------------------------|
# Calendar Post Type Services
#--------------------------------------------------|

# Create a calendar post type
def create_calendar_post_type(post_type_data: schema.CalendarPostTypeCreate, db: Session):

    # calendar_id = None  if post_type_data.is_instant_post else post_type_data.calendar_id
    # calendar_post_id = None  if post_type_data.is_instant_post else post_type_data.calendar_post_id
    # Create the post type object
    post_type = CalendarPostType(
        store_id=post_type_data.store_id,
        branch_id=post_type_data.branch_id,
        user_id=post_type_data.user_id,
        calendar_id=post_type_data.calendar_id,
        calendar_post_id=post_type_data.calendar_post_id,
        connected_account_id=post_type_data.connected_account_id,
        post_type=post_type_data.post_type,
        content=post_type_data.content,
        link=post_type_data.link,
        is_instant_post=post_type_data.is_instant_post
    )
    
    # Attach the image relationships
    if post_type_data.images:
        images = db.query(PostImage).filter(PostImage.id.in_(post_type_data.images)).all()
        post_type.images = images

    db.add(post_type)
    db.commit()
    db.refresh(post_type)
    
    print("post_type ============== ",getattr(post_type.connected_account, "token", None))

    #instant  directly post on the social media
    if post_type.is_instant_post:
        try:
            platform_name = post_type.connected_account.master_account.social_media_name.lower()
            if platform_name == "x (twitter)":
                response = post_to_twitter(post_type)
            elif platform_name in ("facebook", "facebook page"):
                response = post_to_facebook(post_type)
            else:
                response = {"success": False,
                            "error": "Unsupported platform"}

            if response.get("success"):
                post_type.external_post_id = response.get("post_id")
                if post_type.calendar_post:
                    post_type.calendar_post.post_status = "published"
                # post_type.calendar_post.post_status = "published"  # Mark parent post as published
                db.commit()
                logging.info(f"[InstantPost] Posted immediately to {platform_name} with ID {post_type.external_post_id}")
            else:
                logging.error(f"[InstantPost] Failed for {platform_name}: {response.get('error')}")

        except Exception as e:
            logging.error(f"[InstantPost] Error: {str(e)}")

    return post_type



# Get a calendar post type by ID
def get_calendar_post_type_by_id(db: Session, post_type_id: int) -> schema.CalendarPostTypeOut:
    post_type = db.query(CalendarPostType).filter(CalendarPostType.id == post_type_id,CalendarPostType.is_deleted == False).first()
    if not post_type:
        return None
    return schema.CalendarPostTypeOut.model_validate(post_type)



# Get post types by post_id (query param)
def get_post_types_by_post_id(db: Session, post_id: int) -> List[schema.CalendarPostTypeOut]:
    post_types = (
        db.query(CalendarPostType)
        .filter(CalendarPostType.calendar_post_id == post_id, CalendarPostType.is_deleted == False)
        .all()
    )

    if not post_types:
        return []

    return [schema.CalendarPostTypeOut.model_validate(pt) for pt in post_types]



# Update a calendar post type
# def update_calendar_post_type(db: Session, post_type_id: int, post_type_data: schema.CalendarPostTypeUpdate) -> schema.CalendarPostTypeOut:
#     post_type = db.query(CalendarPostType).filter(CalendarPostType.id == post_type_id, CalendarPostType.is_deleted == False).first()
#     if not post_type:
#         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post type not found")
    
#     for key, value in post_type_data.model_dump(exclude_unset=True).items():
#         setattr(post_type, key, value)
    
#     db.commit()
#     db.refresh(post_type)
#     return schema.CalendarPostTypeOut.model_validate(post_type)
def update_calendar_post_type(
    db: Session,
    post_type_id: int,
    post_type_data: schema.CalendarPostTypeUpdate  
) -> schema.CalendarPostTypeOut:
    
    post_type = db.query(CalendarPostType).filter(
        CalendarPostType.id == post_type_id,
        CalendarPostType.is_deleted == False
    ).first()

    if not post_type:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post type not found")

    update_data = post_type_data.model_dump(exclude_unset=True)

    # Handle `images` field separately
    image_ids = update_data.pop("images", None)
    if image_ids is not None:
        images = db.query(PostImage).filter(PostImage.id.in_(image_ids)).all()
        if len(images) != len(image_ids):
            found_ids = {img.id for img in images}
            missing_ids = list(set(image_ids) - found_ids)
            raise HTTPException(
                status_code=400,
                detail=f"Some image IDs are invalid or missing: {missing_ids}"
            )
        post_type.images = images  # This sets the relationship correctly

    # Set other fields dynamically
    for key, value in update_data.items():
        setattr(post_type, key, value)

    # If instant post and not yet published, post directly
    if post_type.is_instant_post and post_type.external_post_id is None:
        post_type.status = "published"
        try:
            platform_name = post_type.connected_account.master_account.social_media_name.lower()
            if platform_name == "x (twitter)":
                response = post_to_twitter(post_type, db)
            elif platform_name in ("facebook", "facebook page"):
                response = post_to_facebook(post_type)
            else:
                response = {"success": False, "error": "Unsupported platform"}

            if response.get("success"):
                post_type.external_post_id = response.get("post_id")
                # Ensure the calendar_post relationship is loaded and bound to session
                # db.refresh(post_type)
                # if post_type.calendar_post:
                #     post_type.calendar_post.post_status = "published"
                logging.info(f"[InstantPost][UPDATE] Posted immediately to {platform_name} with ID {post_type.external_post_id}")
            else:
                logging.error(f"[InstantPost][UPDATE] Failed for {platform_name}: {response.get('error')}")
        except Exception as e:
            logging.error(f"[InstantPost][UPDATE] Error: {str(e)}")

    # Single commit at the end
    db.commit()
    db.refresh(post_type)
    return schema.CalendarPostTypeOut.model_validate(post_type)



# Delete a calendar post type
def delete_calendar_post_type(db: Session, post_type_id: int) -> None:
    post_type = db.query(CalendarPostType).filter(CalendarPostType.id == post_type_id).first()
    if not post_type:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Calendar post type not found")
    
    post_type.is_deleted = True
    db.commit()







#---------------------------------------------------|
# Generate social media post content
#---------------------------------------------------|


#------------------------------Generate Text Content by AI -------------------------------------------------------------


# Initialize Azure OpenAI client
client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    api_version="2024-02-15-preview",
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

def generate_text_content(data: GenerateTextContent) -> GeneratedTextResponse:
    """
    Generate text content using Azure OpenAI based on the GenerateContentRequest schema.
    """

    prompt = (
        f"Generate a compelling social media post for '{data.post_type}'.\n"
        f"Topic: {data.input_text}\n"
        f"Ensure it is professional, engaging, and within {data.text_max_limit} characters.\n"
        f"Try to keep the length around the optimum limit of {data.text_optimum_limit} characters for best engagement."
    )

    try:
        response = client.chat.completions.create(
            model=os.getenv("AZURE_OPENAI_DEPLOYMENT", "gpt-35-turbo"),
            messages=[
                {"role": "system", "content": "You are a helpful social media content creator."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=500,
            temperature=0.7
        )

        ai_text = response.choices[0].message.content.strip()

        # Enforce text_max_limit if provided
        if data.text_max_limit and len(ai_text) > data.text_max_limit:
            ai_text = ai_text[:data.text_max_limit - 3] + "..."

        return GeneratedTextResponse(generated_text=ai_text)

    except Exception as e:
        return GeneratedTextResponse(generated_text=f"Error generating content: {str(e)}")









#-------------------------------Generate Image by AI ----------------------------------------------------------------------
#Generate Image 
def generate_image_content(state: GenerateImageContent, db: Session) -> GeneratedImageResponse:

    # Generate temp name for image file
    image_temp_name = f"{state.post_type}_{state.input_text[:15].replace(' ', '_')}.jpg"
  

    try:
        # Call external API
        response = requests.post(
            settings.IDEOGRAM_URL,
            headers={
                "Api-Key": settings.IDEOGRAM_API_KEY,
            },
            files={
                "prompt": (None, state.input_text),
                "aspect_ratio": (None, state.image_ratio.replace(":","x") or "1x1"),
                "magic_prompt": (None, "ON"),
                'num_images': (None, 1)
            }
        )

        if response.status_code != 200:
            raise ValueError(f"Image generation failed: {response.status_code} - {response.text}")

        #  Parse the API response safely
        data = response.json()
        image_items = data.get("data", [])
        if not image_items or "url" not in image_items[0]:
            raise ValueError(f"No image URLs returned. Full response: {data}")

        external_image_url = image_items[0]["url"]


        # Download image content
        image_content = requests.get(external_image_url, stream=True)
        content_type = image_content.headers.get("Content-Type")

        if content_type not in ["image/png", "image/jpeg", "image/jpg", "image/webp"]:
            raise ValueError("Invalid image type returned from API.")

        #  Generate UUID-based file name
        filename = f"{uuid.uuid4().hex}_{image_temp_name}"
        file_path = os.path.join(STATIC_PATH, filename)

        #  Ensure static directory exists
        os.makedirs(STATIC_PATH, exist_ok=True)

        # Save image locally
        with open(file_path, "wb") as out_file:
            shutil.copyfileobj(image_content.raw, out_file)

        # Build local image URL
        local_image_url = f"{BASE_URL}/static/marketing/images/{filename}"

        #  Save to database
        post_image = PostImage(
            post_id=state.post_id,
            post_type_id = state.post_type_id, 
            image_url=local_image_url,
            image_name=filename,
            created_at=datetime.utcnow()
        )
        db.add(post_image)
        db.commit()
        db.refresh(post_image)

        #  Return Pydantic response
        return GeneratedImageResponse(
            generated_images=[
                GeneratedImage(
                    id=post_image.id,
                    image_url=post_image.image_url,
                    image_name=post_image.image_name
                )
            ]
        )

    except Exception as e:
        raise RuntimeError(f"Image generation failed: {str(e)}")
    





#------------------------------------------------------------------------------------------|
#                           Get URL information
#------------------------------------------------------------------------------------------|

def get_url_info(url: str) -> URLInfoResponse:
    result = {
        'title': None,
        'meta_description': None,
    }
    
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        }
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        
        result['http_headers'] = dict(response.headers)
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # Get title or fallback to first <h1>
        if soup.title and soup.title.string:
            result['title'] = soup.title.string.strip()
        else:
            h1 = soup.find('h1')
            if h1:
                result['title'] = h1.get_text(strip=True)
        
        # Get meta description or fallback to first <p>
        meta_desc = soup.find('meta', attrs={'name': 'description'})
        if meta_desc and meta_desc.get('content'):
            result['meta_description'] = meta_desc.get('content').strip()
        else:
            p = soup.find('p')
            if p:
                result['meta_description'] = p.get_text(strip=True)

    except requests.exceptions.RequestException as e:
        result['error'] = f"Request failed: {str(e)}"
    except Exception as e:
        result['error'] = f"An error occurred: {str(e)}"
        
    return URLInfoResponse(**result)





##------------------------------------------------------------------

# DELETE PHOTOS 
##------------------------------------------------------------------
def delete_upload_photo(db:Session,image_id: int, post_type_id:int):
    search_photo = db.query(PostImage).filter(PostImage.id==image_id,PostImage.post_type_id==post_type_id).first()
    if not search_photo:
       return None
    # search_photo.is_deleted = True
    db.delete(search_photo)
    db.commit()
    return True




