"""
Real Estate Data Utilities
Comprehensive utilities for managing and querying real estate data
"""

import json
import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional


@dataclass
class PropertyFilter:
    """Filter criteria for property searches"""

    min_price: Optional[int] = None
    max_price: Optional[int] = None
    min_bedrooms: Optional[int] = None
    max_bedrooms: Optional[int] = None
    min_bathrooms: Optional[float] = None
    max_bathrooms: Optional[float] = None
    areas: Optional[List[str]] = None
    property_types: Optional[List[str]] = None
    min_sqft: Optional[int] = None
    max_sqft: Optional[int] = None
    features: Optional[List[str]] = None


class RealEstateDataManager:
    """Centralized manager for all real estate data"""

    def __init__(self, data_dir: str = None):
        # Use absolute path relative to this file's location
        if data_dir is None:
            # Get the directory where utils.py is located
            base_dir = Path(__file__).parent.resolve()
            data_dir = base_dir / "data"
        self.data_dir = str(data_dir)
        self._cache = {}
        self._load_all_data()

    def _load_json_file(self, filepath: str) -> Dict[str, Any]:
        """Load JSON file with caching"""
        if filepath in self._cache:
            return self._cache[filepath]

        try:
            with open(filepath, "r", encoding="utf-8") as f:
                data = json.load(f)
                self._cache[filepath] = data
                return data
        except (FileNotFoundError, json.JSONDecodeError) as e:
            # Log the error for debugging
            print(f"Warning: Failed to load {filepath}: {e}", file=sys.stderr)
            return {}

    def _load_all_data(self):
        """Load all data files"""
        self.properties = self._load_json_file(
            os.path.join(self.data_dir, "properties", "active_listings.json")
        )
        self.agents = self._load_json_file(
            os.path.join(self.data_dir, "agents", "agent_profiles.json")
        )
        self.market = self._load_json_file(
            os.path.join(self.data_dir, "market", "market_analytics.json")
        )
        self.clients = self._load_json_file(
            os.path.join(self.data_dir, "clients", "client_database.json")
        )
        self.amenities = self._load_json_file(
            os.path.join(self.data_dir, "amenities", "local_amenities.json")
        )
        self.transactions = self._load_json_file(
            os.path.join(self.data_dir, "transactions", "recent_sales.json")
        )
        self.areas = self._load_json_file(
            os.path.join(self.data_dir, "areas", "city_overview.json")
        )

    def refresh_data(self):
        """Refresh all cached data"""
        self._cache.clear()
        self._load_all_data()

    # Property Operations
    def get_all_properties(self) -> List[Dict[str, Any]]:
        """Get all active property listings"""
        return self.properties.get("active_listings", [])

    def get_property_by_id(self, property_id: str) -> Optional[Dict[str, Any]]:
        """Get a specific property by ID"""
        for prop in self.get_all_properties():
            if prop.get("id") == property_id:
                return prop
        return None

    def filter_properties(self, filters: PropertyFilter) -> List[Dict[str, Any]]:
        """Filter properties based on criteria"""
        properties = self.get_all_properties()
        filtered = []

        for prop in properties:
            if self._matches_filter(prop, filters):
                filtered.append(prop)

        return filtered

    def _matches_filter(self, prop: Dict[str, Any], filters: PropertyFilter) -> bool:
        """Check if property matches filter criteria"""
        # Price range
        if filters.min_price and prop.get("price", 0) < filters.min_price:
            return False
        if filters.max_price and prop.get("price", float("inf")) > filters.max_price:
            return False

        # Bedrooms
        if filters.min_bedrooms and prop.get("bedrooms", 0) < filters.min_bedrooms:
            return False
        if (
            filters.max_bedrooms
            and prop.get("bedrooms", float("inf")) > filters.max_bedrooms
        ):
            return False

        # Bathrooms
        if filters.min_bathrooms and prop.get("bathrooms", 0) < filters.min_bathrooms:
            return False
        if (
            filters.max_bathrooms
            and prop.get("bathrooms", float("inf")) > filters.max_bathrooms
        ):
            return False

        # Areas
        if filters.areas and prop.get("area") not in filters.areas:
            return False

        # Property types
        if (
            filters.property_types
            and prop.get("property_type") not in filters.property_types
        ):
            return False

        # Square footage
        if filters.min_sqft and prop.get("square_feet", 0) < filters.min_sqft:
            return False
        if (
            filters.max_sqft
            and prop.get("square_feet", float("inf")) > filters.max_sqft
        ):
            return False

        # Features
        if filters.features:
            prop_features = prop.get("features", [])
            for required_feature in filters.features:
                if not any(
                    required_feature.lower() in feature.lower()
                    for feature in prop_features
                ):
                    return False

        return True

    def search_properties(self, query: str) -> List[Dict[str, Any]]:
        """Search properties by text query"""
        properties = self.get_all_properties()
        results = []
        query_lower = query.lower()

        for prop in properties:
            # Search in address, description, features, and area
            searchable_text = " ".join(
                [
                    prop.get("address", ""),
                    prop.get("description", ""),
                    prop.get("area", ""),
                    " ".join(prop.get("features", [])),
                    prop.get("property_type", ""),
                    prop.get("style", ""),
                ]
            ).lower()

            if query_lower in searchable_text:
                results.append(prop)

        return results

    def get_properties_by_agent(self, agent_id: str) -> List[Dict[str, Any]]:
        """Get all properties handled by a specific agent"""
        properties = self.get_all_properties()
        return [prop for prop in properties if prop.get("agent_id") == agent_id]

    def get_properties_by_area(self, area: str) -> List[Dict[str, Any]]:
        """Get all properties in a specific area"""
        properties = self.get_all_properties()
        return [
            prop for prop in properties if prop.get("area", "").lower() == area.lower()
        ]

    # Agent Operations
    def get_all_agents(self) -> List[Dict[str, Any]]:
        """Get all agent profiles"""
        return self.agents.get("agents", [])

    def get_agent_by_id(self, agent_id: str) -> Optional[Dict[str, Any]]:
        """Get a specific agent by ID"""
        for agent in self.get_all_agents():
            if agent.get("id") == agent_id:
                return agent
        return None

    def search_agents(self, query: str) -> List[Dict[str, Any]]:
        """Search agents by name, specialization, or area"""
        agents = self.get_all_agents()
        results = []
        query_lower = query.lower()

        for agent in agents:
            searchable_text = " ".join(
                [
                    agent.get("name", ""),
                    " ".join(agent.get("specializations", [])),
                    " ".join(agent.get("expertise_areas", [])),
                    agent.get("bio", ""),
                ]
            ).lower()

            if query_lower in searchable_text:
                results.append(agent)

        return results

    def get_agent_performance(self, agent_id: str) -> Dict[str, Any]:
        """Get agent performance metrics"""
        agent = self.get_agent_by_id(agent_id)
        if not agent:
            return {}

        properties = self.get_properties_by_agent(agent_id)
        recent_sales = agent.get("recent_sales", [])

        return {
            "agent_id": agent_id,
            "name": agent.get("name"),
            "active_listings": len(properties),
            "recent_sales_count": len(recent_sales),
            "avg_days_on_market": self._calculate_avg_days_on_market(recent_sales),
            "total_sales_volume": sum(
                sale.get("sale_price", 0) for sale in recent_sales
            ),
            "specializations": agent.get("specializations", []),
            "client_rating": self._calculate_avg_rating(
                agent.get("client_testimonials", [])
            ),
        }

    def _calculate_avg_days_on_market(self, sales: List[Dict[str, Any]]) -> float:
        """Calculate average days on market for sales"""
        if not sales:
            return 0
        days = [sale.get("days_on_market", 0) for sale in sales]
        return sum(days) / len(days) if days else 0

    def _calculate_avg_rating(self, testimonials: List[Dict[str, Any]]) -> float:
        """Calculate average client rating"""
        if not testimonials:
            return 0
        ratings = [t.get("rating", 0) for t in testimonials]
        return sum(ratings) / len(ratings) if ratings else 0

    # Market Operations
    def get_market_overview(self) -> Dict[str, Any]:
        """Get overall market overview"""
        return self.market.get("market_overview", {})

    def get_area_market_data(self, area: str) -> Optional[Dict[str, Any]]:
        """Get market data for a specific area"""
        area_performance = self.market.get("area_performance", {})
        return area_performance.get(area)

    def get_price_analytics(self) -> Dict[str, Any]:
        """Get price analytics data"""
        return self.market.get("price_analytics", {})

    def get_investment_opportunities(self) -> Dict[str, Any]:
        """Get investment opportunity data"""
        return self.market.get("investment_opportunities", {})

    def compare_areas(self, areas: List[str]) -> Dict[str, Any]:
        """Compare market data across multiple areas"""
        comparison = {}
        area_performance = self.market.get("area_performance", {})

        for area in areas:
            if area in area_performance:
                comparison[area] = area_performance[area]

        return comparison

    # Client Operations
    def get_all_clients(self) -> List[Dict[str, Any]]:
        """Get all clients"""
        return self.clients.get("clients", [])

    def get_client_by_id(self, client_id: str) -> Optional[Dict[str, Any]]:
        """Get a specific client by ID"""
        for client in self.get_all_clients():
            if client.get("id") == client_id:
                return client
        return None

    def get_clients_by_agent(self, agent_id: str) -> List[Dict[str, Any]]:
        """Get all clients for a specific agent"""
        clients = self.get_all_clients()
        return [client for client in clients if client.get("agent_id") == agent_id]

    def match_clients_to_properties(self, client_id: str) -> List[Dict[str, Any]]:
        """Match properties to client preferences"""
        client = self.get_client_by_id(client_id)
        if not client or client.get("type") != "Buyer":
            return []

        preferences = client.get("preferences", {})
        budget = preferences.get("budget_range", {})

        filters = PropertyFilter(
            min_price=budget.get("min"),
            max_price=budget.get("max"),
            areas=preferences.get("desired_areas"),
            property_types=(
                [preferences.get("property_type")]
                if preferences.get("property_type")
                else None
            ),
        )

        return self.filter_properties(filters)

    # Amenities Operations
    def get_schools_by_area(self, area: str) -> List[Dict[str, Any]]:
        """Get schools near a specific area"""
        # This is a simplified implementation - in reality you'd use geographic data
        all_schools = []
        schools_data = self.amenities.get("schools", {})

        for school_type in [
            "elementary_schools",
            "middle_schools",
            "high_schools",
            "private_schools",
        ]:
            schools = schools_data.get(school_type, [])
            all_schools.extend(schools)

        return all_schools  # In a real implementation, filter by proximity to area

    def get_amenities_by_type(self, amenity_type: str) -> List[Dict[str, Any]]:
        """Get amenities by type (parks, shopping, healthcare, etc.)"""
        return self.amenities.get(amenity_type, {})

    def get_area_amenities(self, area: str) -> Dict[str, Any]:
        """Get all amenities for a specific area"""
        amenities = {}

        # Schools
        amenities["schools"] = self.get_schools_by_area(area)

        # Parks
        parks = self.amenities.get("parks_and_recreation", {}).get("parks", [])
        amenities["parks"] = [
            park for park in parks if park.get("area", "").lower() == area.lower()
        ]

        # Shopping
        shopping = self.amenities.get("shopping", {})
        area_shopping = {}
        for shop_type, shops in shopping.items():
            if isinstance(shops, list):
                area_shopping[shop_type] = [
                    shop
                    for shop in shops
                    if shop.get("area", "").lower() == area.lower()
                ]
        amenities["shopping"] = area_shopping

        return amenities

    # Transaction Operations
    def get_recent_sales(self) -> List[Dict[str, Any]]:
        """Get all recent sales"""
        return self.transactions.get("recent_sales", [])

    def get_sales_by_area(self, area: str) -> List[Dict[str, Any]]:
        """Get recent sales in a specific area"""
        sales = self.get_recent_sales()
        return [sale for sale in sales if sale.get("area", "").lower() == area.lower()]

    def get_sales_by_agent(self, agent_id: str) -> List[Dict[str, Any]]:
        """Get recent sales by a specific agent"""
        sales = self.get_recent_sales()
        return [sale for sale in sales if sale.get("agent_id") == agent_id]

    def calculate_market_trends(self, area: str = None) -> Dict[str, Any]:
        """Calculate market trends based on recent sales"""
        sales = self.get_sales_by_area(area) if area else self.get_recent_sales()

        if not sales:
            return {}

        total_sales = len(sales)
        avg_sale_price = sum(sale.get("sale_price", 0) for sale in sales) / total_sales
        avg_days_on_market = (
            sum(sale.get("days_on_market", 0) for sale in sales) / total_sales
        )
        avg_price_per_sqft = (
            sum(sale.get("price_per_sqft", 0) for sale in sales) / total_sales
        )

        return {
            "total_sales": total_sales,
            "average_sale_price": avg_sale_price,
            "average_days_on_market": avg_days_on_market,
            "average_price_per_sqft": avg_price_per_sqft,
            "area": area or "All Areas",
        }

    # Area Operations
    def get_all_areas(self) -> List[Dict[str, Any]]:
        """Get all area information"""
        return self.areas.get("areas", [])

    def get_area_info(self, area_name: str) -> Optional[Dict[str, Any]]:
        """Get detailed information about a specific area"""
        areas = self.get_all_areas()
        for area in areas:
            if area.get("name", "").lower() == area_name.lower():
                return area
        return None

    def get_city_overview(self) -> Dict[str, Any]:
        """Get overall city information"""
        return {
            "city_name": self.areas.get("city_name"),
            "state": self.areas.get("state"),
            "population": self.areas.get("population"),
            "median_income": self.areas.get("median_income"),
            "school_districts": self.areas.get("school_districts", []),
            "market_trends": self.areas.get("market_trends", {}),
        }

    # Cross-referencing and Analytics
    def get_comprehensive_area_report(self, area: str) -> Dict[str, Any]:
        """Get comprehensive report for an area including properties, market data, amenities"""
        return {
            "area_info": self.get_area_info(area),
            "market_data": self.get_area_market_data(area),
            "active_properties": self.get_properties_by_area(area),
            "recent_sales": self.get_sales_by_area(area),
            "amenities": self.get_area_amenities(area),
            "market_trends": self.calculate_market_trends(area),
        }

    def get_agent_dashboard(self, agent_id: str) -> Dict[str, Any]:
        """Get comprehensive dashboard for an agent"""
        agent = self.get_agent_by_id(agent_id)
        if not agent:
            return {}

        return {
            "agent_info": agent,
            "performance": self.get_agent_performance(agent_id),
            "active_listings": self.get_properties_by_agent(agent_id),
            "clients": self.get_clients_by_agent(agent_id),
            "recent_sales": self.get_sales_by_agent(agent_id),
        }

    def get_property_insights(self, property_id: str) -> Dict[str, Any]:
        """Get detailed insights for a property including comparable sales and area info"""
        prop = self.get_property_by_id(property_id)
        if not prop:
            return {}

        area = prop.get("area")
        agent = self.get_agent_by_id(prop.get("agent_id"))

        return {
            "property": prop,
            "agent": agent,
            "area_info": self.get_area_info(area),
            "area_market_data": self.get_area_market_data(area),
            "comparable_sales": self.get_sales_by_area(area),
            "area_amenities": self.get_area_amenities(area),
        }


# Global instance
data_manager = RealEstateDataManager()
