import datetime
import uuid
from enum import Enum

class UsageType(Enum):
    API_CALL = "API Call"
    EMBEDDED_SYSTEM = "Embedded System"
    INTERNAL_USE = "Internal Use"

class LicenseType(Enum):
    PER_VALIDATION = "Per Validation"
    SAAS_TIER = "SaaS Tier"
    ENTERPRISE = "Enterprise"
    WHITE_LABEL = "White Label"


class Patent:
    def __init__(self, patent_id, title, description, filing_date, expiry_date):
        self.patent_id = patent_id
        self.title = title
        self.description = description
        self.filing_date = filing_date
        self.expiry_date = expiry_date
        self.roi = 0.0  # Return on Investment
        self.licensing_revenue = 0.0
        self.development_cost = 0.0 # Track initial development cost


    def __repr__(self):
        return f"Patent(ID: {self.patent_id}, Title: {self.title})"

class UsageRecord:
    def __init__(self, record_id, user_id, patent_id, timestamp, usage_type, details=None):
        self.record_id = record_id or str(uuid.uuid4())  # Generate unique ID if not provided
        self.user_id = user_id
        self.patent_id = patent_id
        self.timestamp = timestamp
        self.usage_type = usage_type
        self.details = details or {} # Additional details about the usage (e.g., API call parameters)
        self.cost = 0.0  # Cost associated with this usage record


    def __repr__(self):
        return f"UsageRecord(ID: {self.record_id}, User: {self.user_id}, Patent: {self.patent_id}, Type: {self.usage_type})"

class PricingModel:
    def __init__(self, license_type, pricing_details):
        self.license_type = license_type
        self.pricing_details = pricing_details  # Dictionary containing pricing parameters


    def calculate_cost(self, usage_record):
        """Calculates the cost based on the usage record and pricing details."""
        if self.license_type == LicenseType.PER_VALIDATION:
            return self.pricing_details.get("price_per_validation", 0.10)  # Default to $0.10 per validation
        elif self.license_type == LicenseType.SAAS_TIER:
            # Example:  Pricing based on number of validations per month
            tier = self.determine_saas_tier(usage_record.user_id)
            if tier == "Free":
                return 0.0
            elif tier == "Basic":
                return self.pricing_details.get("basic_monthly_fee", 100.0) / self.get_monthly_usage_count(usage_record.user_id) #distribute monthly cost over usage
            elif tier == "Premium":
                return self.pricing_details.get("premium_monthly_fee", 500.0) / self.get_monthly_usage_count(usage_record.user_id) #distribute monthly cost over usage
            else:
                return 0.0
        elif self.license_type == LicenseType.ENTERPRISE:
            # Enterprise pricing is usually negotiated, so we'll just return a pre-agreed upon amount.
            return self.pricing_details.get("enterprise_license_fee", 10000.0) / self.get_monthly_usage_count(usage_record.user_id) # distribute monthly cost over usage
        elif self.license_type == LicenseType.WHITE_LABEL:
            # White label pricing is usually a fixed fee.
            return self.pricing_details.get("white_label_license_fee", 50000.0) / self.get_monthly_usage_count(usage_record.user_id) # distribute monthly cost over usage
        else:
            return 0.0

    def determine_saas_tier(self, user_id):
        """Placeholder for SaaS tier determination logic.  Should query database or other system."""
        # In a real implementation, this would query a database or other system
        # to determine the user's SaaS tier.  For now, we'll just return a random tier.
        import random
        tiers = ["Free", "Basic", "Premium"]
        return random.choice(tiers)

    def get_monthly_usage_count(self, user_id):
         """Placeholder for getting the usage count for a given user for the current month."""
         # In a real implementation, this would query a database or other system
         # to determine the user's monthly usage count. For now, we'll just return a random number.
         import random
         return random.randint(10, 1000)


class User:
    def __init__(self, user_id, name, email, license_type, pricing_model):
        self.user_id = user_id
        self.name = name
        self.email = email
        self.license_type = license_type
        self.pricing_model = pricing_model
        self.usage_records = [] # Store usage records associated with the user

    def __repr__(self):
        return f"User(ID: {self.user_id}, Name: {self.name})"

class Invoice:
    def __init__(self, invoice_id, user_id, issue_date, due_date, total_amount, usage_records):
        self.invoice_id = invoice_id or str(uuid.uuid4())
        self.user_id = user_id
        self.issue_date = issue_date
        self.due_date = due_date
        self.total_amount = total_amount
        self.usage_records = usage_records # List of UsageRecord objects included in the invoice

    def __repr__(self):
        return f"Invoice(ID: {self.invoice_id}, User: {self.user_id}, Amount: {self.total_amount})"


class PatentRevenueEngine:
    def __init__(self):
        self.patents = {}  # patent_id: Patent object
        self.usage_records = [] # List of UsageRecord objects
        self.users = {} # user_id: User object
        self.invoices = {} # invoice_id: Invoice object
        self.pricing_models = {} #license_type: PricingModel object

    def register_patent(self, patent):
        """Registers a new patent in the system."""
        if patent.patent_id in self.patents:
            raise ValueError(f"Patent with ID {patent.patent_id} already exists.")
        self.patents[patent.patent_id] = patent

    def record_usage(self, user_id, patent_id, timestamp, usage_type, details=None):
        """Records the usage of a patented validation method."""
        if patent_id not in self.patents:
            raise ValueError(f"Patent with ID {patent_id} not found.")

        if user_id not in self.users:
            raise ValueError(f"User with ID {user_id} not found.")

        usage_record = UsageRecord(None, user_id, patent_id, timestamp, usage_type, details)
        self.usage_records.append(usage_record)
        self.users[user_id].usage_records.append(usage_record)  # Associate usage with the user
        return usage_record

    def register_user(self, user):
        """Registers a new user."""
        if user.user_id in self.users:
            raise ValueError(f"User with ID {user.user_id} already exists.")
        self.users[user.user_id] = user

    def define_pricing_model(self, pricing_model):
        """Defines a pricing model for a given license type."""
        if pricing_model.license_type in self.pricing_models:
            raise ValueError(f"Pricing model for {pricing_model.license_type} already exists.")
        self.pricing_models[pricing_model.license_type] = pricing_model

    def calculate_licensing_fee(self, usage_record):
        """Calculates the licensing fee for a given usage record."""
        user = self.users.get(usage_record.user_id)
        if not user:
            raise ValueError(f"User with ID {usage_record.user_id} not found.")

        if not user.pricing_model:
            raise ValueError(f"No pricing model defined for user {usage_record.user_id}.")
        # Calculate the cost using the pricing model
        cost = user.pricing_model.calculate_cost(usage_record)
        usage_record.cost = cost # Store the cost on the usage record
        return cost

    def generate_invoice(self, user_id, issue_date, due_date):
        """Generates an invoice for a commercial API user."""
        user = self.users.get(user_id)
        if not user:
            raise ValueError(f"User with ID {user_id} not found.")

        # Filter usage records for this user that haven't been invoiced yet.
        # In a real system, this would likely involve a database query and a flag
        # on the UsageRecord to indicate if it has been invoiced.
        unbilled_usage_records = [record for record in self.usage_records if record.user_id == user_id and record.cost == 0.0]

        total_amount = 0.0
        for record in unbilled_usage_records:
            total_amount += self.calculate_licensing_fee(record)

        if total_amount == 0.0:
            print("No new usage records to invoice for this user.")
            return None  # Or raise an exception

        invoice = Invoice(None, user_id, issue_date, due_date, total_amount, unbilled_usage_records)
        self.invoices[invoice.invoice_id] = invoice

        print(f"Generated invoice {invoice.invoice_id} for User {user_id} for ${total_amount:.2f}")
        return invoice

    def analyze_roi_per_patent(self, patent_id):
        """Analyzes the return on investment (ROI) for a given patent."""
        patent = self.patents.get(patent_id)
        if not patent:
            raise ValueError(f"Patent with ID {patent_id} not found.")

        # Calculate total revenue generated by the patent
        total_revenue = sum(record.cost for record in self.usage_records if record.patent_id == patent_id)
        patent.licensing_revenue = total_revenue

        # ROI calculation: (Revenue - Development Cost) / Development Cost
        if patent.development_cost == 0:
            patent.roi = float('inf') if total_revenue > 0 else 0.0 # Handle zero development cost
        else:
            patent.roi = (total_revenue - patent.development_cost) / patent.development_cost

        print(f"ROI for Patent {patent_id}: {patent.roi:.2f}")
        return patent.roi

    def recommend_licensing_strategy(self, patent_id):
        """Recommends an optimal licensing strategy based on usage and ROI."""
        patent = self.patents.get(patent_id)
        if not patent:
            raise ValueError(f"Patent with ID {patent_id} not found.")

        roi = patent.roi

        # Simple example logic - can be expanded with more sophisticated analysis
        if roi > 1.0:
            return "Continue with current licensing strategy.  Explore enterprise or white-label options."
        elif roi > 0.5:
            return "Consider increasing per-validation pricing or shifting users to subscription tiers."
        else:
            return "Evaluate the patent's market potential.  Consider alternative licensing strategies or reassess development costs."

    def get_patent_revenue(self, patent_id):
        """Returns the total revenue generated by a given patent."""
        patent = self.patents.get(patent_id)
        if not patent:
            raise ValueError(f"Patent with ID {patent_id} not found.")

        total_revenue = sum(record.cost for record in self.usage_records if record.patent_id == patent_id)
        return total_revenue

    def get_all_usage_records_for_patent(self, patent_id):
        """Returns all usage records for a given patent."""
        return [record for record in self.usage_records if record.patent_id == patent_id]

    def get_all_usage_records_for_user(self, user_id):
        """Returns all usage records for a given user."""
        return [record for record in self.usage_records if record.user_id == user_id]


# Example Usage
if __name__ == "__main__":
    engine = PatentRevenueEngine()

    # 1. Register Patents
    patent1 = Patent("P123", "Validation Method A", "...", datetime.date(2020, 1, 1), datetime.date(2040, 1, 1))
    patent1.development_cost = 10000  # Initial development cost
    engine.register_patent(patent1)

    patent2 = Patent("P456", "Validation Method B", "...", datetime.date(2021, 5, 10), datetime.date(2041, 5, 10))
    patent2.development_cost = 15000
    engine.register_patent(patent2)

    # 2. Define Pricing Models
    per_validation_pricing = PricingModel(LicenseType.PER_VALIDATION, {"price_per_validation": 0.05})
    engine.define_pricing_model(per_validation_pricing)

    saas_pricing = PricingModel(LicenseType.SAAS_TIER, {"basic_monthly_fee": 100.0, "premium_monthly_fee": 500.0})
    engine.define_pricing_model(saas_pricing)

    enterprise_pricing = PricingModel(LicenseType.ENTERPRISE, {"enterprise_license_fee": 10000.0})
    engine.define_pricing_model(enterprise_pricing)

    white_label_pricing = PricingModel(LicenseType.WHITE_LABEL, {"white_label_license_fee": 50000.0})
    engine.define_pricing_model(white_label_pricing)


    # 3. Register Users
    user1 = User("U101", "Acme Corp", "acme@example.com", LicenseType.PER_VALIDATION, per_validation_pricing)
    engine.register_user(user1)

    user2 = User("U102", "Beta Inc", "beta@example.com", LicenseType.SAAS_TIER, saas_pricing)
    engine.register_user(user2)

    user3 = User("U103", "Gamma Ltd", "gamma@example.com", LicenseType.ENTERPRISE, enterprise_pricing)
    engine.register_user(user3)

    user4 = User("U104", "Delta Co", "delta@example.com", LicenseType.WHITE_LABEL, white_label_pricing)
    engine.register_user(user4)


    # 4. Record Usage
    usage1 = engine.record_usage("U101", "P123", datetime.datetime.now(), UsageType.API_CALL, {"api_endpoint": "/validate"})
    usage2 = engine.record_usage("U101", "P123", datetime.datetime.now(), UsageType.API_CALL, {"api_endpoint": "/validate"})
    usage3 = engine.record_usage("U102", "P123", datetime.datetime.now(), UsageType.API_CALL, {"api_endpoint": "/validate"})
    usage4 = engine.record_usage("U103", "P123", datetime.datetime.now(), UsageType.EMBEDDED_SYSTEM, {"device_id": "D001"})
    usage5 = engine.record_usage("U104", "P123", datetime.datetime.now(), UsageType.INTERNAL_USE)
    usage6 = engine.record_usage("U101", "P456", datetime.datetime.now(), UsageType.API_CALL)
    usage7 = engine.record_usage("U102", "P456", datetime.datetime.now(), UsageType.API_CALL)
    usage8 = engine.record_usage("U103", "P456", datetime.datetime.now(), UsageType.EMBEDDED_SYSTEM)
    usage9 = engine.record_usage("U104", "P456", datetime.datetime.now(), UsageType.INTERNAL_USE)

    # 5. Generate Invoices
    invoice1 = engine.generate_invoice("U101", datetime.date.today(), datetime.date.today() + datetime.timedelta(days=30))
    invoice2 = engine.generate_invoice("U102", datetime.date.today(), datetime.date.today() + datetime.timedelta(days=30))
    invoice3 = engine.generate_invoice("U103", datetime.date.today(), datetime.date.today() + datetime.timedelta(days=30))
    invoice4 = engine.generate_invoice("U104", datetime.date.today(), datetime.date.today() + datetime.timedelta(days=30))


    # 6. Analyze ROI
    engine.analyze_roi_per_patent("P123")
    engine.analyze_roi_per_patent("P456")

    # 7. Recommend Licensing Strategy
    recommendation1 = engine.recommend_licensing_strategy("P123")
    print(f"Licensing Recommendation for P123: {recommendation1}")
    recommendation2 = engine.recommend_licensing_strategy("P456")
    print(f"Licensing Recommendation for P456: {recommendation2}")

    # 8. Get Patent Revenue
    revenue_p123 = engine.get_patent_revenue("P123")
    print(f"Revenue for P123: ${revenue_p123:.2f}")
    revenue_p456 = engine.get_patent_revenue("P456")
    print(f"Revenue for P456: ${revenue_p456:.2f}")

    # 9. Get Usage Records
    usage_records_p123 = engine.get_all_usage_records_for_patent("P123")
    print(f"Usage Records for P123: {usage_records_p123}")

    usage_records_u101 = engine.get_all_usage_records_for_user("U101")
    print(f"Usage Records for U101: {usage_records_u101}")
