"""
Database models for Wakelai Voice AI SaaS Platform
Multi-tenant architecture with proper isolation
"""
from datetime import datetime, timedelta
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import db, login_manager
import secrets
import string


class TimestampMixin:
    """Mixin for timestamp fields"""
    created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)


class User(UserMixin, TimestampMixin, db.Model):
    """User model - SaaS platform users"""
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True, nullable=False, index=True)
    username = db.Column(db.String(50), unique=True, nullable=False, index=True)
    password_hash = db.Column(db.String(255))

    # OAuth fields
    google_id = db.Column(db.String(255), unique=True, index=True)
    google_email = db.Column(db.String(255))
    google_picture = db.Column(db.String(500))

    # User details
    full_name = db.Column(db.String(100))
    personal_email = db.Column(db.String(255))
    email_preference = db.Column(db.String(20), default='primary')  # primary, personal, both

    # Account status
    is_active = db.Column(db.Boolean, default=True, nullable=False)
    is_admin = db.Column(db.Boolean, default=False, nullable=False)
    email_verified = db.Column(db.Boolean, default=False, nullable=False)

    # Subscription
    subscription_tier = db.Column(db.String(20), default='free', nullable=False)  # free, pro, scale
    subscription_expires_at = db.Column(db.DateTime)
    stripe_customer_id = db.Column(db.String(255))
    stripe_subscription_id = db.Column(db.String(255))

    # Relationships
    instances = db.relationship('Instance', back_populates='user', cascade='all, delete-orphan')
    usage_records = db.relationship('UsageRecord', back_populates='user', cascade='all, delete-orphan')

    def set_password(self, password):
        """Set password hash"""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """Check password against hash"""
        if not self.password_hash:
            return False
        return check_password_hash(self.password_hash, password)

    def get_max_instances(self):
        """Get max instances based on subscription tier"""
        limits = {
            'free': db.session.query(db.func.coalesce(db.func.max(
                db.column('FREE_PLAN_INSTANCE_LIMIT'), 1
            )).label('limit')).first()[0] if db.session.query(
                db.column('FREE_PLAN_INSTANCE_LIMIT')
            ).first() else 1,
            'pro': 3,
            'scale': 10
        }
        return limits.get(self.subscription_tier, 1)

    def is_subscribed(self):
        """Check if user has active subscription"""
        if self.subscription_tier == 'free':
            return True
        if self.subscription_expires_at:
            return self.subscription_expires_at > datetime.utcnow()
        return False

    def __repr__(self):
        return f'<User {self.username}>'


class Instance(TimestampMixin, db.Model):
    """Instance model - Per-tenant voice AI instances"""
    __tablename__ = 'instances'

    id = db.Column(db.Integer, primary_key=True)
    instance_id = db.Column(db.String(36), unique=True, nullable=False, index=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    # Instance details
    name = db.Column(db.String(100), nullable=False)
    subdomain = db.Column(db.String(50), unique=True, nullable=False, index=True)
    description = db.Column(db.Text)

    # Instance configuration
    port = db.Column(db.Integer)
    status = db.Column(db.String(20), default='provisioning', nullable=False)  # provisioning, running, stopped, error
    container_id = db.Column(db.String(100))  # Docker container ID

    # Configuration storage
    config = db.Column(db.JSON)  # Store instance-specific configuration

    # Agent settings
    agent_persona = db.Column(db.String(50), default='jarvis')
    voice_provider = db.Column(db.String(50), default='supertonic')
    llm_provider = db.Column(db.String(50), default='anthropic')

    # Features
    enable_google_calendar = db.Column(db.Boolean, default=False)
    enable_gmail = db.Column(db.Boolean, default=False)
    enable_canvas = db.Column(db.Boolean, default=True)
    enable_3d_ui = db.Column(db.Boolean, default=True)
    enable_music_generation = db.Column(db.Boolean, default=False)

    # Google tokens (encrypted at rest in production)
    google_access_token = db.Column(db.Text)
    google_refresh_token = db.Column(db.Text)
    google_token_expires_at = db.Column(db.DateTime)

    # Relationships
    user = db.relationship('User', back_populates='instances')
    conversations = db.relationship('Conversation', back_populates='instance', cascade='all, delete-orphan')
    usage_records = db.relationship('UsageRecord', back_populates='instance', cascade='all, delete-orphan')

    @staticmethod
    def generate_instance_id():
        """Generate unique instance ID"""
        while True:
            instance_id = secrets.token_urlsafe(16)
            if not Instance.query.filter_by(instance_id=instance_id).first():
                return instance_id

    @staticmethod
    def generate_subdomain(name):
        """Generate unique subdomain from name"""
        base = ''.join(c for c in name.lower() if c in string.ascii_lowercase + string.digits)
        if not base:
            base = 'instance'

        subdomain = base
        counter = 1
        while Instance.query.filter_by(subdomain=subdomain).first():
            subdomain = f"{base}{counter}"
            counter += 1
        return subdomain

    def get_url(self):
        """Get instance URL"""
        return f"{self.subdomain}.wakelai.com"

    def __repr__(self):
        return f'<Instance {self.name}>'


class Conversation(TimestampMixin, db.Model):
    """Conversation model - Chat history per instance"""
    __tablename__ = 'conversations'

    id = db.Column(db.Integer, primary_key=True)
    instance_id = db.Column(db.Integer, db.ForeignKey('instances.id'), nullable=False)
    session_id = db.Column(db.String(36), nullable=False, index=True)

    # Message details
    role = db.Column(db.String(20), nullable=False)  # user, assistant, system
    content = db.Column(db.Text, nullable=False)

    # Metadata
    tokens_used = db.Column(db.Integer)
    model_used = db.Column(db.String(50))
    processing_time_ms = db.Column(db.Integer)

    # Features used
    used_canvas = db.Column(db.Boolean, default=False)
    used_calendar = db.Column(db.Boolean, default=False)
    used_gmail = db.Column(db.Boolean, default=False)
    used_music = db.Column(db.Boolean, default=False)

    # Relationships
    instance = db.relationship('Instance', back_populates='conversations')

    def __repr__(self):
        return f'<Conversation {self.session_id} - {self.role}>'


class UsageRecord(TimestampMixin, db.Model):
    """Usage record model - Track usage for billing"""
    __tablename__ = 'usage_records'

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    instance_id = db.Column(db.Integer, db.ForeignKey('instances.id'), nullable=False)

    # Usage metrics
    date = db.Column(db.Date, nullable=False, index=True)
    total_messages = db.Column(db.Integer, default=0)
    total_tokens = db.Column(db.Integer, default=0)
    total_canvas_pages = db.Column(db.Integer, default=0)
    total_minutes_active = db.Column(db.Integer, default=0)

    # API usage
    llm_api_calls = db.Column(db.Integer, default=0)
    tts_api_calls = db.Column(db.Integer, default=0)
    stt_api_calls = db.Column(db.Integer, default=0)

    # Relationships
    user = db.relationship('User', back_populates='usage_records')
    instance = db.relationship('Instance', back_populates='usage_records')

    def __repr__(self):
        return f'<UsageRecord {self.user_id} - {self.date}>'


class OAuthState(db.Model):
    """OAuth state model - Security for OAuth flows"""
    __tablename__ = 'oauth_states'

    id = db.Column(db.Integer, primary_key=True)
    state = db.Column(db.String(255), unique=True, nullable=False, index=True)
    provider = db.Column(db.String(20), nullable=False)  # google
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    instance_id = db.Column(db.Integer, db.ForeignKey('instances.id'))
    expires_at = db.Column(db.DateTime, nullable=False)
    redirect_uri = db.Column(db.String(500))

    @staticmethod
    def generate_state():
        """Generate secure state token"""
        return secrets.token_urlsafe(32)

    def is_valid(self):
        """Check if state is valid and not expired"""
        return self.expires_at > datetime.utcnow()

    def __repr__(self):
        return f'<OAuthState {self.provider}>'


# Flask-Login user loader
@login_manager.user_loader
def load_user(user_id):
    """Load user by ID for Flask-Login"""
    return User.query.get(int(user_id))
