import asyncio
import datetime
import logging
import os
import sys
from datetime import datetime, timedelta, timezone

from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field

sys.path.append(".")
from configs.config import OPENAI_MODEL_MINI, openai_api_key
from utils.dynamo_db import DynamoDB

# Default recipient
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "sagar.soni@xcapmarket.com")
RECIPIENT_EMAIL = "sagar.soni@xcapmarket.com"  # Replace with the actual recipient email


# Configure AWS DynamoDB
DYNAMODB_TABLE = "press_releases"
dynamodb = DynamoDB()
table = dynamodb.dynamodb.Table(DYNAMODB_TABLE)

### words to check
words_to_check = [
    "merger",
    "acquisition",
    "m&a",
    "merge",
    "acquire",
    "buyout",
    "buy back",
    "buybacks",
    "buyback",
    "takeover",
    "hostile takeover",
    "friendly takeover",
    "leveraged buyout",
    "lbo",
    "purchase",
    "consolidation",
    "joint venture",
    "tie-up",
    "roll-up",
    "combine",
]


class BlogPost(BaseModel):
    title: str = Field(..., description="The title of the blog post")
    content: str = Field(..., description="The content of the blog post")


class LinkedinPost(BaseModel):
    title: str = Field(..., description="The title of the linkedin post")
    content: str = Field(..., description="The content of the linkedin post")
    hashtags: list[str] = Field(..., description="The hashtag of the linkedin post")


# Function to query DynamoDB for the last week's press releases
def get_last_releases(days=1):
    """Query DynamoDB for the last week's press releases"""
    time_delta = datetime.now(timezone.utc) - timedelta(days=days)
    items = dynamodb.get_all_table_items(table)
    last_week_items = [
        item
        for item in items
        if datetime.fromisoformat(item["created_date"]) >= time_delta
        and item["relevant_PR"] == True
    ]
    return last_week_items


# Function to filter M&A related press releases
async def filter_ma_releases(items):
    """Filter M&A related press releases"""
    ma_items = [
        item
        for item in items
        if any(word in item["summary"].lower() for word in words_to_check)
    ]

    llm_ma_items = await check_if_PR_is_ma(ma_items)
    return llm_ma_items


# Function to create a LinkedIn post
async def create_linkedin_post(ma_items):
    """Create a LinkedIn post"""

    async def process_item(item):
        item["linkedin_post"] = await convert_pr_to_linkedin_post(item)
        item["blog_post"] = await convert_pr_to_blog_post(item)
        return item

    ma_items = await asyncio.gather(*(process_item(item) for item in ma_items))
    return ma_items


async def check_if_PR_is_ma(items: list[dict]):
    """Check if the press release is M&A related"""
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "You are an investment banker looking to create a linkedin post. \
            You are going to be provided a Press Release, you need to figure out if the post \
            would be valuable for your clients."
                "",
            ),
            (
                "human",
                "Is this press release united states company M&A related, return only True or False",
            ),
            ("human", "{summary}"),
        ]
    )
    llm = ChatOpenAI(
        model_name=OPENAI_MODEL_MINI, openai_api_key=openai_api_key, temperature=0.5
    )
    chain = prompt | llm
    chain_output = await chain.abatch([{"summary": item["summary"]} for item in items])
    PR_output = []
    for i, result in enumerate(chain_output):
        if "true" in result.content.lower():
            PR_output.append(items[i])
    return PR_output


async def convert_pr_to_linkedin_post(item):
    """Create a LinkedIn post"""
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "You are an investment banker looking to create a linkedin post. \
            You are going to be provided a Press Release, You need to find the relevant information to convert it into a linkedin post. \
            You want to find information about the target company, acquirer and the rationale for the acquisition. \
            Ensure the post has financial information about the transaction.",
            ),
            ("human", "{pr_text}"),
            ("system", "The JSON output is structured like this {json_output}"),
        ]
    )
    llm = ChatOpenAI(
        model_name="gpt-4o", openai_api_key=openai_api_key, temperature=0.5
    )
    parser = JsonOutputParser(pydantic_object=LinkedinPost)
    chain = prompt | llm | parser
    chain_output = await chain.ainvoke(
        {"pr_text": item["text"], "json_output": parser.get_format_instructions()}
    )
    return LinkedinPost(**chain_output).model_dump()


async def convert_pr_to_blog_post(item):
    """Create a blog post"""
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "You are an investment banker looking to create a blog post. \
            You are going to be provided a Press Release, You need to find the relevant information to convert it into a blog post. \
            You want to find information about the target company, acquirer and the rationale for the acquisition. \
            Ensure the post has financial information about the transaction."
                "",
            ),
            ("human", "{pr_text}"),
            ("system", "The JSON output is structured like this {json_output}"),
        ]
    )
    llm = ChatOpenAI(
        model_name="gpt-4o", openai_api_key=openai_api_key, temperature=0.5
    )
    parser = JsonOutputParser(pydantic_object=BlogPost)
    chain = prompt | llm | parser
    chain_output = await chain.ainvoke(
        {"pr_text": item["text"], "json_output": parser.get_format_instructions()}
    )
    return BlogPost(**chain_output).model_dump()


def create_email_body(items):
    """Create an email body"""
    email_body = "<html><body>"
    # add the date
    email_body += f"<h1>{datetime.now().strftime('%Y-%m-%d')}</h1>"
    # add the linkedin post

    for item in items:
        item_body = f"<h3><a href='{item['url']}'>{item['title']}</a></h3>"
        item_body += f"<p>Summary: {item['summary']}</p>"
        item_body += f"<p>Linkedin Post</p><p>Title: {item['linkedin_post']['title']}</p><p>{item['linkedin_post']['content']}</p><p>{item['linkedin_post']['hashtags']}</p>"
        item_body += f"<p>Blog Post</p><p>Title: {item['blog_post']['title']}</p><p>{item['blog_post']['content']}</p>"
        email_body += item_body

    email_body += "</body></html>"
    return email_body


def send_email(subject="Daily M&A Press Release Summary", items=None):
    # Send an email using the Graph Mail API
    from utils.aws_email_service import send_email

    send_email(
        sender=SMTP_USERNAME,
        recipients=[RECIPIENT_EMAIL],
        subject=subject,
        body_html=create_email_body(items),
    )


def handler(event, context):
    """Main function to execute the script"""

    last_week_releases = get_last_releases(days=1)
    ma_releases = asyncio.run(filter_ma_releases(last_week_releases))

    linkedin_post = asyncio.run(create_linkedin_post(ma_releases))
    logging.info(linkedin_post)
    send_email("Daily LinkedIn Post", linkedin_post)


# Main function to execute the script
if __name__ == "__main__":
    handler(None, None)
