import json
import os
import copy
import cohere
from elasticsearch.helpers import bulk
from elasticsearch import Elasticsearch
import anthropic
import google.generativeai as genai
from datetime import datetime
import re
import base64
from io import BytesIO
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

# Configuration
CONFIG = {
    "LLM_PROVIDER": "claude",  # Options: "claude", "gemini"
    "ELASTICSEARCH_HOST": "http://localhost:9200",
    "REDIS_HOST": "localhost",
    "REDIS_PORT": 6379,
    "REDIS_DB": 0,
    "INDEX_NAME": "tender_analysis_index",
    "TOP_MATCHES": 10,
    "RERANK_TOP_N": 15
}

# API Keys
COHERE_API_KEY = "Az1yGMw8rORRk87fO38h9fpmwqJ6ZY9IRA1ZGjEo"
CLAUDE_API_KEY = "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"
GEMINI_API_KEY = "your-gemini-api-key-here"  # Replace with your actual Gemini API key

# Initialize clients
es_client = Elasticsearch(CONFIG["ELASTICSEARCH_HOST"])
co = cohere.Client(COHERE_API_KEY)
claude_client = anthropic.Anthropic(api_key=CLAUDE_API_KEY)
genai.configure(api_key=GEMINI_API_KEY)

claude_model = "claude-sonnet-4-20250514"

def convert_markdown_to_html(text):
    """
    Convert basic markdown formatting to HTML
    
    Args:
        text (str): Text with markdown formatting
        
    Returns:
        str: HTML formatted text
    """
    
    # Convert headings (# ## ###)
    text = re.sub(r'^### (.*?)$', r'<h3>\1</h3>', text, flags=re.MULTILINE)
    text = re.sub(r'^## (.*?)$', r'<h2>\1</h2>', text, flags=re.MULTILINE)
    text = re.sub(r'^# (.*?)$', r'<h1>\1</h1>', text, flags=re.MULTILINE)
    
    # Convert bold text (**text**)
    text = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', text)
    
    # Convert italic text (*text*) - but not if it's part of **text**
    text = re.sub(r'(?<!\*)\*([^*]+?)\*(?!\*)', r'<em>\1</em>', text)
    
    # Convert line breaks to <br> tags
    # text = text.replace('\n', '<br>\n')
    
    # Add paragraph tags for better structure
    text = f'<div>{text}</div>'
    
    return text

def format_analysis_report(analysis_text):
    """
    Format the analysis report with proper HTML structure
    
    Args:
        analysis_text (str): Raw analysis text with markdown
        
    Returns:
        str: Formatted HTML
    """
    
    # Convert markdown to HTML
    html_text = convert_markdown_to_html(analysis_text)
    
    # Add some basic styling
    formatted_html = f"""
    <div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px;">
        {html_text}
    </div>
    """
    
    return formatted_html


class TenderAnalysisSystem:
    def __init__(self, config=CONFIG):
        self.config = config
        self.es_client = es_client
        self.cohere_client = co
        self.claude_client = claude_client
        
    def encode_cohere(self, texts):
        """Encode texts using Cohere embeddings"""
        print(f'Encoding {len(texts)} input texts')
        
        response = self.cohere_client.embed(
            texts=texts,
            model="embed-v4.0",
            input_type="search_document",
            embedding_types=['float']
        )
        return response.embeddings.float

    def create_index_mapping(self):
        """Create optimized Elasticsearch index mapping for tender analysis"""
        mapping = {
            "settings": {
                "number_of_shards": 1,
                "number_of_replicas": 0,
                "analysis": {
                    "analyzer": {
                        "tender_analyzer": {
                            "type": "custom",
                            "tokenizer": "standard",
                            "filter": ["lowercase", "stop"]
                        }
                    }
                }
            },
            "mappings": {
                "properties": {
                    "tagName": {
                        "type": "keyword"
                    },
                    "question": {
                        "type": "text",
                        "analyzer": "tender_analyzer"
                    },
                    "answer": {
                        "type": "text",
                        "analyzer": "tender_analyzer"
                    },
                    "question_vector": {
                        "type": "dense_vector",
                        "dims": 1536
                    },
                    "created_at": {
                        "type": "date"
                    }
                }
            }
        }
        return mapping

    def get_existing_tag_names(self):
        """Get all existing tagNames from the index"""
        existing_tags = set()
        
        if not self.es_client.indices.exists(index=self.config['INDEX_NAME']):
            return existing_tags
        
        # Scroll through all documents to get tagNames
        try:
            search_body = {
                "query": {"match_all": {}},
                "_source": ["tagName"],
                "size": 1000
            }
            
            # Use scroll API for large datasets
            response = self.es_client.search(
                index=self.config['INDEX_NAME'],
                body=search_body,
                scroll="5m"
            )
            
            # Process first batch
            for hit in response['hits']['hits']:
                existing_tags.add(hit['_source']['tagName'])
            
            # Continue scrolling if there are more results
            while len(response['hits']['hits']) > 0:
                scroll_id = response['_scroll_id']
                response = self.es_client.scroll(scroll_id=scroll_id, scroll="5m")
                
                for hit in response['hits']['hits']:
                    existing_tags.add(hit['_source']['tagName'])
                
                if len(response['hits']['hits']) == 0:
                    break
            
            print(f"Found {len(existing_tags)} existing tender entries in index")
            
        except Exception as e:
            print(f"Error retrieving existing tagNames: {e}")
            
        return existing_tags

    def incremental_index_tender_data(self, data_file_path):
        """Incrementally index new tender data without duplicates"""
        print(f"Starting incremental indexing for: {self.config['INDEX_NAME']}")
        
        # Create index if it doesn't exist
        if not self.es_client.indices.exists(index=self.config['INDEX_NAME']):
            print("Index doesn't exist. Creating new index...")
            mapping = self.create_index_mapping()
            self.es_client.indices.create(
                index=self.config['INDEX_NAME'], 
                body=mapping
            )
        else:
            print("Index exists. Performing incremental update...")
        
        # Get existing tagNames to avoid duplicates
        existing_tags = self.get_existing_tag_names()
        
        # Load new training data
        with open(data_file_path, 'r', encoding='utf-8') as f:
            tender_data = json.load(f)
        
        if not tender_data:
            print("No data found in the training file")
            return {"indexed": 0, "skipped": 0, "total": 0}
        
        # Filter out already indexed tenders
        new_tenders = []
        skipped_count = 0
        
        for tender in tender_data:
            if tender['tagName'] in existing_tags:
                skipped_count += 1
                print(f"Skipping already indexed tender: {tender['tagName']}")
            else:
                new_tenders.append(tender)
        
        if not new_tenders:
            print("No new tenders to index. All tenders already exist in the index.")
            return {"indexed": 0, "skipped": skipped_count, "total": len(tender_data)}
        
        print(f"Found {len(new_tenders)} new tenders to index (skipping {skipped_count} existing)")
        
        # Prepare questions for embedding
        questions = []
        processed_docs = []
        
        for tender in new_tenders:
            # Handle question field (list or string)
            if isinstance(tender['question'], list):
                question_text = " ".join(tender['question'])
            else:
                question_text = tender['question']
            
            questions.append(question_text)
            
            processed_doc = {
                'tagName': tender['tagName'],
                'question': question_text,
                'answer': tender['answer'],
                'created_at': datetime.now().isoformat()
            }
            processed_docs.append(processed_doc)
        
        # Generate embeddings for new tenders only
        print(f"Generating embeddings for {len(questions)} new tenders...")
        question_embeddings = self.encode_cohere(questions)
        
        # Prepare documents for bulk indexing
        requests = []
        for i, doc in enumerate(processed_docs):
            request = doc.copy()
            request["_op_type"] = "index"
            request["_index"] = self.config['INDEX_NAME']
            request["question_vector"] = question_embeddings[i]
            requests.append(request)
        
        # Bulk index new documents
        if requests:
            bulk(self.es_client, requests)
            self.es_client.indices.refresh(index=self.config['INDEX_NAME'])
            print(f"✅ Successfully indexed {len(requests)} new tender documents")
        
        return {
            "indexed": len(requests),
            "skipped": skipped_count,
            "total": len(tender_data)
        }

    def get_index_status(self):
        """Get current index status and statistics"""
        if not self.es_client.indices.exists(index=self.config['INDEX_NAME']):
            return {
                "exists": False,
                "total_documents": 0,
                "index_size": "0",
                "created_date": None
            }
        
        # Get index stats
        stats = self.es_client.indices.stats(index=self.config['INDEX_NAME'])
        doc_count = stats['indices'][self.config['INDEX_NAME']]['total']['docs']['count']
        size_in_bytes = stats['indices'][self.config['INDEX_NAME']]['total']['store']['size_in_bytes']
        
        # Get index creation time
        settings = self.es_client.indices.get_settings(index=self.config['INDEX_NAME'])
        creation_date = settings[self.config['INDEX_NAME']]['settings']['index'].get('creation_date')
        
        if creation_date:
            creation_date = datetime.fromtimestamp(int(creation_date) / 1000).strftime('%Y-%m-%d %H:%M:%S')
        
        return {
            "exists": True,
            "total_documents": doc_count,
            "index_size": f"{size_in_bytes / (1024*1024):.2f} MB",
            "created_date": creation_date
        }

    def search_similar_tenders(self, query_text):
        """Search for similar tenders using vector similarity"""
        print(f"Searching for similar tenders to: {query_text[:100]}...")
        
        # Generate embedding for query
        query_embedding = self.encode_cohere([query_text])[0]
        
        # Elasticsearch query with cosine similarity
        search_body = {
            "size": self.config['RERANK_TOP_N'],
            "query": {
                "script_score": {
                    "query": {"match_all": {}},
                    "script": {
                        "source": "cosineSimilarity(params.query_vector, 'question_vector') + 1.0",
                        "params": {"query_vector": query_embedding}
                    }
                }
            },
            "_source": ["tagName", "question", "answer"],
            "sort": [
                {"_score": {"order": "desc"}}
            ]
        }
        
        # Execute search
        es_response = self.es_client.search(
            index=self.config['INDEX_NAME'],
            body=search_body
        )
        
        # Extract documents for reranking
        documents = []
        doc_metadata = []
        
        for hit in es_response['hits']['hits']:
            documents.append(hit['_source']['answer'])
            doc_metadata.append({
                'tagName': hit['_source']['tagName'],
                'question': hit['_source']['question'],
                'answer': hit['_source']['answer'],
                'es_score': hit['_score']
            })
        
        # Rerank using Cohere
        if documents:
            print("Reranking results using Minaions m-ranks model...")
            reranked_response = self.cohere_client.rerank(
                query=query_text,
                documents=documents,
                top_n=self.config['TOP_MATCHES'],
                model="rerank-v3.5"
            )
            
            # Get top reranked results
            top_results = []
            for result in reranked_response.results:
                original_doc = doc_metadata[result.index]
                original_doc['rerank_score'] = result.relevance_score
                top_results.append(original_doc)
            
            return top_results
        
        return []

    def generate_analysis_prompt(self, current_rfp, similar_tenders):
        """Generate comprehensive analysis prompt for LLM"""
        
        # Prepare context from similar tenders
        context_sections = []
        for i, tender in enumerate(similar_tenders, 1):
            context_sections.append(f"""
TENDER {i} (Tag: {tender['tagName']}, Similarity Score: {tender['rerank_score']:.3f}):
{tender['answer']}
""")
        
        context = "\n".join(context_sections)
        
        prompt = f"""You are an expert procurement analyst and strategic bidding consultant with deep expertise in government tender analysis, pricing strategies, and competitive intelligence.

**CURRENT RFP/TENDER DETAILS:**
{current_rfp}

**HISTORICAL SIMILAR TENDERS FOR ANALYSIS:**
{context}

**ANALYSIS REQUIREMENTS:**
Provide a comprehensive strategic bidding analysis report that includes:

1. **SCOPE SIMILARITY ANALYSIS:**
   - Detailed comparison of current RFP scope with historical similar tenders
   - Identification and explanation of key similarities and differences
   - Relevance scoring of historical data to current tender
   - Mention bid ids or bid numbers of similar tenders found with percentage of similarty found with them
   - Be very rational, thorough and strict while concluding a similarty percentage between current and any previous bid. Dont just consider similarity by key words matching. 

2. **COMPETITIVE LANDSCAPE ANALYSIS:**
   - Analysis of typical competitors in similar tenders
   - Competitor behavior patterns and strategies
   - Market concentration and competition intensity

3. **PRICING STRATEGY RECOMMENDATIONS:**
   - Price range analysis from historical data of all similar tenders
   - L1-L2 price gap analysis to understand competitive aggressiveness
   - Recommended pricing approach with rationale. Do not assume price of items or services unless you find exactly same item price in any historical bid data.
   - Estimation of current RFP price range by deeply comparing and analyzing its scope against previous similar tenders scope and pricing. You can take the price indication from all the previous similar bids and not necessarily only from the top similar bid.
   - If still concrete evidence for price recommentation is not available then it is OK to not predicting any recommended price range and rather suggesting the user to study the matching bids themselves.

4. **BIDDING STRATEGY INSIGHTS:**
   - Key success factors identified from winning bids
   - Common failure patterns to avoid
   - Specific recommendations for current tender
   - Risk mitigation strategies

5. **RISK ASSESSMENT:**
   - Technical risks and mitigation strategies
   - Commercial risks and pricing implications
   - Compliance and documentation requirements

6. **CONFIDENCE ASSESSMENT:**
   - Overall confidence level (Low/Medium/High) with percentage
   - Reliability of analysis based on data quality and similarity
   - Key assumptions and limitations

**OUTPUT FORMAT:**
Provide your analysis in a structured format that can be easily converted to HTML with the following sections:
- Executive Summary with the bid id or bid number of similar tenders found. Mention the dates or timeframe when these bids were opened. Suggest the user to study these similar bids and their winning participant sellers on their own also. And while recommending the price range (if at all you arrive at one), mention that this range is indicative only with AI logic application on whatever similar tenders found.
- Detailed Analysis (organized by the 6 categories above)
- Data-driven insights with specific numbers and percentages
- Clear recommendations of price range supported with rational behind this recommendation
- Confidence scoring with justification

"""
        return prompt

    def get_llm_response(self, prompt):
        """Get response from selected LLM (Claude or Gemini)"""
        
        if self.config["LLM_PROVIDER"] == "claude":
            response = self.claude_client.messages.create(
                model=claude_model,
                max_tokens=4000,
                temperature=0.2,
                system="You are an expert procurement analyst and strategic bidding consultant. Provide detailed, data-driven analysis with actionable insights.",
                messages=[{"role": "user", "content": prompt}]
            )
            return response.content[0].text
            
        elif self.config["LLM_PROVIDER"] == "gemini":
            model = genai.GenerativeModel('gemini-1.5-pro')
            response = model.generate_content(
                prompt,
                generation_config=genai.types.GenerationConfig(
                    max_output_tokens=4000,
                    temperature=0.2,
                )
            )
            return response.text
        
        else:
            raise ValueError(f"Unsupported LLM provider: {self.config['LLM_PROVIDER']}")

    def create_price_analysis_chart(self, tender_data):
        """Create price analysis chart from tender data"""
        plt.style.use('seaborn-v0_8')
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
        
        # Extract price data
        prices = []
        tender_names = []
        l1_l2_gaps = []
        
        for tender in tender_data:
            # Extract price information using regex
            price_matches = re.findall(r'Rs\.?\s*(\d+(?:,\d+)*(?:\.\d+)?)', tender['answer'])
            if len(price_matches) >= 2:
                l1_price = float(price_matches[0].replace(',', ''))
                l2_price = float(price_matches[1].replace(',', ''))
                
                prices.append(l1_price)
                tender_names.append(tender['tagName'][:15] + '...')
                l1_l2_gaps.append(((l2_price - l1_price) / l1_price) * 100)
        
        if prices:
            # Price distribution chart
            ax1.bar(range(len(prices)), prices, alpha=0.7, color='steelblue')
            ax1.set_title('L1 Winning Prices Across Similar Tenders', fontsize=14, fontweight='bold')
            ax1.set_xlabel('Tender')
            ax1.set_ylabel('Price (Rs.)')
            ax1.tick_params(axis='x', rotation=45)
            
            # L1-L2 gap analysis
            ax2.bar(range(len(l1_l2_gaps)), l1_l2_gaps, alpha=0.7, color='coral')
            ax2.set_title('L1-L2 Price Gap (% above L1)', fontsize=14, fontweight='bold')
            ax2.set_xlabel('Tender')
            ax2.set_ylabel('Price Gap (%)')
            ax2.tick_params(axis='x', rotation=45)
        
        plt.tight_layout()
        
        # Convert to base64 for HTML embedding
        buffer = BytesIO()
        plt.savefig(buffer, format='png', dpi=300, bbox_inches='tight')
        buffer.seek(0)
        chart_data = base64.b64encode(buffer.getvalue()).decode()
        plt.close()
        
        return chart_data

    def create_competitor_analysis_chart(self, tender_data):
        """Create competitor analysis chart"""
        plt.style.use('seaborn-v0_8')
        fig, ax = plt.subplots(figsize=(12, 8))
        
        # Extract competitor information
        competitors = {}
        for tender in tender_data:
            # Extract company names (basic pattern matching)
            companies = re.findall(r'([A-Z][A-Z\s&]+(?:ENTERPRISES|INDUSTRIES|COMPANY|CO|LTD|PRIVATE|CORPORATION|AGENCY|SERVICES|SOLUTIONS|TRADERS|STORES))', tender['answer'])
            for company in companies:
                company = company.strip()
                if len(company) > 5:  # Filter out short matches
                    competitors[company] = competitors.get(company, 0) + 1
        
        if competitors:
            # Get top 10 competitors
            top_competitors = sorted(competitors.items(), key=lambda x: x[1], reverse=True)[:10]
            
            companies, frequencies = zip(*top_competitors)
            
            ax.barh(companies, frequencies, alpha=0.7, color='lightgreen')
            ax.set_title('Frequent Competitors in Similar Tenders', fontsize=14, fontweight='bold')
            ax.set_xlabel('Frequency of Participation')
            ax.set_ylabel('Company')
        
        plt.tight_layout()
        
        # Convert to base64
        buffer = BytesIO()
        plt.savefig(buffer, format='png', dpi=300, bbox_inches='tight')
        buffer.seek(0)
        chart_data = base64.b64encode(buffer.getvalue()).decode()
        plt.close()
        
        return chart_data

    def generate_html_report(self, analysis_text, tender_data, current_rfp):
        """Generate comprehensive HTML report with charts and analysis"""
        
        # Generate charts
        price_chart = self.create_price_analysis_chart(tender_data)
        competitor_chart = self.create_competitor_analysis_chart(tender_data)
        
        # Calculate summary statistics
        total_tenders = len(tender_data)
        avg_similarity = sum(t['rerank_score'] for t in tender_data) / total_tenders if tender_data else 0
        
        html_template = f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Strategic Tender Analysis Report</title>
    <style>
        * {{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }}
        
        body {{
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }}
        
        .container {{
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }}
        
        .report-header {{
            background: white;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
            margin-bottom: 30px;
            text-align: center;
        }}
        
        .report-header h1 {{
            color: #2c3e50;
            font-size: 2.5em;
            margin-bottom: 10px;
            font-weight: 700;
        }}
        
        .report-header .subtitle {{
            color: #7f8c8d;
            font-size: 1.2em;
            margin-bottom: 20px;
        }}
        
        .stats-grid {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 20px;
            margin-top: 20px;
        }}
        
        .stat-card {{
            background: linear-gradient(135deg, #3498db, #2980b9);
            color: white;
            padding: 20px;
            border-radius: 10px;
            text-align: center;
        }}
        
        .stat-number {{
            font-size: 2em;
            font-weight: bold;
            display: block;
        }}
        
        .content-grid {{
            display: grid;
            grid-template-columns: 1fr;
            gap: 30px;
        }}
        
        .analysis-section {{
            background: white;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 5px 20px rgba(0,0,0,0.1);
        }}
        
        .analysis-section h2 {{
            color: #2c3e50;
            border-bottom: 3px solid #3498db;
            padding-bottom: 10px;
            margin-bottom: 20px;
            font-size: 1.8em;
        }}
        
        .chart-container {{
            text-align: center;
            margin: 20px 0;
        }}
        
        .chart-container img {{
            max-width: 100%;
            border-radius: 10px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }}
        
        .rfp-details {{
            background: #ecf0f1;
            padding: 20px;
            border-radius: 10px;
            margin-bottom: 20px;
        }}
        
        .rfp-details h3 {{
            color: #2c3e50;
            margin-bottom: 15px;
        }}
        
        .analysis-content {{
            white-space: pre-wrap;
            font-size: 1.1em;
            line-height: 1.8;
        }}
        
        .highlight {{
            background: linear-gradient(120deg, #a8e6cf 0%, #88c999 100%);
            padding: 2px 6px;
            border-radius: 4px;
        }}
        
        .warning {{
            background: linear-gradient(120deg, #ffd93d 0%, #ff6b6b 100%);
            color: white;
            padding: 15px;
            border-radius: 8px;
            margin: 15px 0;
        }}
        
        .success {{
            background: linear-gradient(120deg, #56ab2f 0%, #a8e6cf 100%);
            color: white;
            padding: 15px;
            border-radius: 8px;
            margin: 15px 0;
        }}
        
        @media (max-width: 768px) {{
            .container {{
                padding: 10px;
            }}
            
            .report-header h1 {{
                font-size: 2em;
            }}
            
            .stats-grid {{
                grid-template-columns: 1fr;
            }}
        }}
    </style>
</head>
<body>
    <div class="container">
        <div class="report-header">
            <h1>🎯 Strategic Tender Analysis Report</h1>
            <div class="subtitle">AI-Powered Bidding Intelligence & Strategy Recommendations</div>
            <div class="subtitle">Generated on {datetime.now().strftime('%B %d, %Y at %H:%M')}</div>
            
            <div class="stats-grid">
                <div class="stat-card">
                    <span class="stat-number">{total_tenders}</span>
                    <span>Similar Tenders Analyzed</span>
                </div>
                <div class="stat-card">
                    <span class="stat-number">{avg_similarity:.1%}</span>
                    <span>Average Similarity Score</span>
                </div>
                <div class="stat-card">
                    <span class="stat-number">Minaions</span>
                    <span>AI Analysis Engine</span>
                </div>
            </div>
        </div>
        
        <div class="content-grid">
            <div class="analysis-section">
                <h2>📋 Current RFP Details</h2>
                <div class="rfp-details">
                    <h3>Tender Scope & Requirements:</h3>
                    <div class="analysis-content">{current_rfp[:1000]}{'...' if len(current_rfp) > 1000 else ''}</div>
                </div>
            </div>
            
            <div class="analysis-section">
                <h2>📊 Price Analysis Dashboard</h2>
                <div class="chart-container">
                    <img src="data:image/png;base64,{price_chart}" alt="Price Analysis Chart" />
                </div>
            </div>
            
            <div class="analysis-section">
                <h2>🏢 Competitor Landscape Analysis</h2>
                <div class="chart-container">
                    <img src="data:image/png;base64,{competitor_chart}" alt="Competitor Analysis Chart" />
                </div>
            </div>
            
            <div class="analysis-section">
                <h2>🧠 AI Strategic Analysis</h2>
                <div class="analysis-content">{analysis_text}</div>
            </div>
        </div>
    </div>
</body>
</html>
"""
        return html_template

    def analyze_tender(self, current_rfp_text):
        """Main function to perform comprehensive tender analysis (without indexing)"""
        print("🚀 Starting comprehensive tender analysis...")
        
        # Check if index exists
        if not self.es_client.indices.exists(index=self.config['INDEX_NAME']):
            raise Exception(f"Index '{self.config['INDEX_NAME']}' does not exist. Please run indexing first using 'incremental_index_tender_data()' method.")
        
        # Get index status
        index_status = self.get_index_status()
        print(f"📊 Using existing index with {index_status['total_documents']} tender documents")
        
        # Step 1: Search for similar tenders
        print("🔍 Searching for similar historical tenders...")
        similar_tenders = self.search_similar_tenders(current_rfp_text)
        
        if not similar_tenders:
            print("⚠️ No similar tenders found")
            return None
        
        print(f"✅ Found {len(similar_tenders)} similar tenders")
        
        # Step 2: Generate analysis prompt
        print("📝 Looking into details of similar tenders...")
        analysis_prompt = self.generate_analysis_prompt(current_rfp_text, similar_tenders)
        
        # Step 3: Get LLM analysis
        print(f"🤖 Minaions doing the strategic analysis...")
        analysis_response = self.get_llm_response(analysis_prompt)
        
        # Step 4: Generate HTML report
        print("📄 Generating comprehensive structured report...")
        html_report = self.generate_html_report(format_analysis_report(analysis_response), similar_tenders, current_rfp_text)
        
        # Step 5: Save report
        report_filename = f"tender_analysis_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
        with open(report_filename, 'w', encoding='utf-8') as f:
            f.write(html_report)
        
        print(f"✅ Analysis complete! Report saved as: {report_filename}")
        
        return {
            'analysis': analysis_response,
            'similar_tenders': similar_tenders,
            'html_report_path': report_filename,
            'html_content': html_report,
            'index_status': index_status
        }

# Separate Index Management Class
class TenderIndexManager:
    """Separate class for managing tender data indexing"""
    
    def __init__(self, config=CONFIG):
        self.config = config
        self.analyzer = TenderAnalysisSystem(config)
    
    def create_or_update_index(self, embeddings_file_path):
        """Create or incrementally update the tender index"""
        print("🔄 Starting index creation/update process...")
        
        # Check current index status
        index_status = self.analyzer.get_index_status()
        
        if index_status['exists']:
            print(f"📊 Current index status:")
            print(f"   • Total documents: {index_status['total_documents']}")
            print(f"   • Index size: {index_status['index_size']}")
            print(f"   • Created: {index_status['created_date']}")
        else:
            print("📊 No existing index found. Will create new index.")
        
        # Perform incremental indexing
        result = self.analyzer.incremental_index_tender_data(embeddings_file_path)
        
        print("\n" + "="*60)
        print("🎉 INDEXING COMPLETED!")
        print("="*60)
        print(f"📈 New tenders indexed: {result['indexed']}")
        print(f"⏭️  Existing tenders skipped: {result['skipped']}")
        print(f"📊 Total tenders in file: {result['total']}")
        
        # Get updated index status
        updated_status = self.analyzer.get_index_status()
        print(f"💾 Index now contains: {updated_status['total_documents']} documents")
        print(f"💽 Index size: {updated_status['index_size']}")
        print("="*60)
        
        return result
    
    def rebuild_index(self, embeddings_file_path):
        """Completely rebuild the index (use with caution)"""
        print("⚠️  WARNING: This will completely rebuild the index!")
        confirm = input("Are you sure? Type 'YES' to confirm: ")
        
        if confirm == "YES":
            # Delete existing index
            if self.analyzer.es_client.indices.exists(index=self.config['INDEX_NAME']):
                self.analyzer.es_client.indices.delete(index=self.config['INDEX_NAME'])
                print("🗑️  Deleted existing index")
            
            # Create new index and index all data
            return self.create_or_update_index(embeddings_file_path)
        else:
            print("❌ Rebuild cancelled")
            return None
    
    def get_index_info(self):
        """Get detailed information about the current index"""
        status = self.analyzer.get_index_status()
        
        if not status['exists']:
            print("❌ Index does not exist")
            return None
        
        print("📊 CURRENT INDEX STATUS")
        print("="*40)
        print(f"Index Name: {self.config['INDEX_NAME']}")
        print(f"Total Documents: {status['total_documents']}")
        print(f"Index Size: {status['index_size']}")
        print(f"Created Date: {status['created_date']}")
        
        # Get sample of recent entries
        try:
            recent_docs = self.analyzer.es_client.search(
                index=self.config['INDEX_NAME'],
                body={
                    "query": {"match_all": {}},
                    "sort": [{"created_at": {"order": "desc"}}],
                    "size": 5,
                    "_source": ["tagName", "created_at"]
                }
            )
            
            if recent_docs['hits']['hits']:
                print("\n🕒 Recent Entries:")
                for hit in recent_docs['hits']['hits']:
                    print(f"   • {hit['_source']['tagName']} ({hit['_source']['created_at'][:10]})")
        except Exception as e:
            print(f"Error retrieving recent entries: {e}")
        
        print("="*40)
        return status

# Usage example and main function
def main():
    """Example usage of the Tender Analysis System with separate indexing"""
    
    # Path to the embeddings training file
    embeddings_file = "bids_embeddings_training_for_indexing.json"
    
    print("🚀 TENDER ANALYSIS SYSTEM")
    print("="*50)
    print("1. Index Management (Run when you have new data)")
    print("2. Tender Analysis (Run for each analysis request)")
    print("="*50)
    
    # Example 1: Index Management (run periodically when new data arrives)
    print("\n📊 STEP 1: MANAGING INDEX")
    print("-" * 30)
    
    # Initialize index manager
    index_manager = TenderIndexManager()
    
    # Check current index status
    index_manager.get_index_info()
    
    # Create or update index with new data
    indexing_result = index_manager.create_or_update_index(embeddings_file)
    
    # Example 2: Tender Analysis (run for each user request)
    print("\n🔍 STEP 2: PERFORMING ANALYSIS")
    print("-" * 30)
    
    # Initialize analyzer
    analyzer = TenderAnalysisSystem()
    
    # Example current RFP text
    current_rfp = """
    Bid Number: GEM/2025/B/6500123. This bid is for the following categories: Office Stationery Items including 
    A4 Size Paper 75 GSM, Ball Pens Blue and Black, File Folders, Envelopes various sizes, Registers and 
    Record Books. The total quantity required is 5000 units. The bid opening date is 25-07-2025 12:00:00. 
    The buyer is located at New Delhi. This procurement is by the Ministry of Defence. The procuring 
    department is Department of Military Affairs. The organisation is Indian Army. Project Scope: 
    Procurement of office stationery items for administrative purposes with delivery to be completed 
    within 30 days of award. Quality specifications require ISI mark for paper products and branded 
    stationery items from approved vendors.
    """
    
    try:
        # Perform analysis (no indexing required here)
        result = analyzer.analyze_tender(current_rfp)
        
        if result:
            print("\n" + "="*80)
            print("🎉 ANALYSIS COMPLETED SUCCESSFULLY!")
            print("="*80)
            print(f"📊 Analyzed {len(result['similar_tenders'])} similar tenders")
            print(f"📄 HTML report generated: {result['html_report_path']}")
            print(f"🤖 Analysis engine: {CONFIG['LLM_PROVIDER'].title()}")
            print(f"💾 Index contains: {result['index_status']['total_documents']} total documents")
            print("="*80)
            
            return result
        else:
            print("❌ Analysis failed - no similar tenders found")
            return None
            
    except Exception as e:
        print(f"❌ Error during analysis: {str(e)}")
        if "does not exist" in str(e):
            print("💡 Tip: Run indexing first using TenderIndexManager.create_or_update_index()")
        return None

def demo_indexing_workflow():
    """Demonstrate the indexing workflow for new data"""
    print("📚 INDEXING WORKFLOW DEMONSTRATION")
    print("="*50)
    
    embeddings_file = "bids_embeddings_training_for_indexing.json"
    
    # Initialize index manager
    index_manager = TenderIndexManager()
    
    # Step 1: Check current status
    print("\n1️⃣ Checking current index status...")
    index_manager.get_index_info()
    
    # Step 2: Perform incremental indexing
    print("\n2️⃣ Performing incremental indexing...")
    result = index_manager.create_or_update_index(embeddings_file)
    
    # Step 3: Verify results
    print("\n3️⃣ Verifying indexing results...")
    if result['indexed'] > 0:
        print(f"✅ Successfully added {result['indexed']} new tenders")
    if result['skipped'] > 0:
        print(f"⏭️ Skipped {result['skipped']} existing tenders (no duplicates)")
    
    return result

def demo_analysis_workflow():
    """Demonstrate the analysis workflow"""
    print("🔍 ANALYSIS WORKFLOW DEMONSTRATION")
    print("="*50)
    
    # Sample RFP for analysis
    sample_rfp = """This bid is for the following categories: High Density Polyethylene Bucket,Domestic Mugs - Bathroom,Waste Containers and Accessories - Domestic (V2). The total quantity required is 1384 units. The bid opening date is 11-07-2025 19:30:00. The buyer is located at Kumar Abhay Ranjan,buycon178.ad.bh@gembuyer.in,Joint Agriculture Building, Mathurapur, Bazar Samittee, Samastipur 848101,Samastipur,BIHAR,848101,India,8676-885150-. The procuring department is Agriculture Department Bihar. The organisation is Agriculture Department. The office is District Agriculture Office Samastipur Mathurapur. \nOther important details about the nature of this bid are as following:\n Risk Factors: **Option Clause:** \"The Purchaser reserves the right to increase or decrease the quantity to be ordered up to 25 percent of bid quantity at the time of placement of contract. The purchaser also reserves the right to increase the ordered quantity up to 25% of the contracted quantity during the currency of the contract at the contracted rates.\" **Cancellation Risk:** \"Buyer reserve right to cancel bid at any stage without any reason.\" **Transportation Risk:** \"Seller has to transport all item with own cost at consinee location or Provided Location. There is no any extra transportation cost will be pay.\" **Jurisdiction:** \"Subjected to Samastipur Jurisdiction Only.\" **Blacklisting:** \"Black Listed company / Enterprises etc not allowed to participate in this Bid.\" **Contract Termination:** \"any false declaration and non-compliance of this would be a ground for immediate termination of the contract and further legal action in accordance with the laws.\" **Document Rejection:** \"without self-attested documents will not be considered and bidder's bid will be rejected.
    """
    
    # Initialize analyzer
    analyzer = TenderAnalysisSystem()
    
    try:
        # Perform analysis
        print("🤖 Running AI-powered tender analysis...")
        result = analyzer.analyze_tender(sample_rfp)
        
        if result:
            print(f"\n✅ Analysis completed successfully!")
            print(f"📊 Found {len(result['similar_tenders'])} similar historical tenders")
            print(f"📄 Report saved: {result['html_report_path']}")
            
            # Show similarity scores
            print("\n🎯 Top Similar Tenders:")
            for i, tender in enumerate(result['similar_tenders'][:5], 1):
                print(f"   {i}. {tender['tagName']} (Similarity: {tender['rerank_score']:.3f})")
            
            return result
        else:
            print("❌ No similar tenders found for analysis")
            return None
            
    except Exception as e:
        print(f"❌ Analysis failed: {str(e)}")
        return None

if __name__ == "__main__":
    # Configuration options
    CONFIG["LLM_PROVIDER"] = "claude"  # Change to "gemini" if needed
    
    print("🎯 TENDER ANALYSIS SYSTEM - PRODUCTION READY")
    print("="*60)
    print("Choose workflow:")
    print("1. Full Demo (Indexing + Analysis)")
    print("2. Indexing Only")
    print("3. Analysis Only")
    print("="*60)
    
    choice = input("Enter your choice (1-3): ").strip()
    
    if choice == "1":
        # Full workflow demonstration
        indexing_result = demo_indexing_workflow()
        if indexing_result:
            print("\n" + "="*60)
            analysis_result = demo_analysis_workflow()
    
    elif choice == "2":
        # Indexing only
        demo_indexing_workflow()
    
    elif choice == "3":
        # Analysis only
        demo_analysis_workflow()
    
    else:
        # Default: run full workflow
        main()