from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from datetime import datetime, date

from ..extensions import db
from .base import BaseModel


class User(UserMixin, BaseModel):
    """User model for Australian construction marketplace workers and contractors"""
    __tablename__ = 'users'

    # Basic Information
    username = db.Column(db.String(80))  # Added to match database schema
    email = db.Column(db.String(120), unique=True, nullable=False, index=True)
    password_hash = db.Column(db.String(255), nullable=False)
    first_name = db.Column(db.String(80), nullable=False)
    last_name = db.Column(db.String(80), nullable=False)
    role = db.Column(db.String(20), nullable=False)  # 'worker' or 'contractor'

    # Contact Information
    phone_number = db.Column(db.String(20), nullable=False)
    location = db.Column(db.String(200), nullable=False)

    # Australian Business Information (Legal Compliance)
    business_name = db.Column(db.String(200))
    primary_trade = db.Column(db.String(100))
    secondary_trade = db.Column(db.String(500))
    abn_number = db.Column(db.String(11), unique=True, nullable=False, index=True)
    gst_registered = db.Column(db.Boolean, default=False, nullable=False)

    # Work Statistics & Performance
    jobs_completed = db.Column(db.Integer, default=0, nullable=False)
    average_rating = db.Column(db.Numeric(3, 2), default=0.0)
    total_reviews = db.Column(db.Integer, default=0, nullable=False)
    response_rate = db.Column(db.Numeric(5, 2), default=0.0)

    # Australian Insurance & Compliance Requirements
    public_liability_insurance = db.Column(db.Boolean, default=False, nullable=False)
    public_liability_amount = db.Column(db.Numeric(12, 2))
    workers_comp_insurance = db.Column(db.Boolean, default=False, nullable=False)
    insurance_expiry_date = db.Column(db.Date)

    # WHS (Workplace Health & Safety) Compliance
    worker_control_level = db.Column(db.String(50))
    white_card_number = db.Column(db.String(50))
    white_card_expiry = db.Column(db.Date)

    # Account Management
    is_active = db.Column(db.Boolean, default=True, nullable=False)
    account_status = db.Column(db.String(20), default='active', nullable=False)
    privacy_consent = db.Column(db.Boolean, default=False, nullable=False)
    terms_accepted = db.Column(db.Boolean, default=False, nullable=False)
    terms_accepted_date = db.Column(db.DateTime)
    date_created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    last_login = db.Column(db.DateTime)
    
    # Soft Delete & Data Retention (GDPR/Privacy Act Compliance)
    is_deleted = db.Column(db.Boolean, default=False, nullable=False, index=True)
    deleted_at = db.Column(db.DateTime, nullable=True)
    anonymised_at = db.Column(db.DateTime, nullable=True)
    scheduled_hard_delete_at = db.Column(db.DateTime, nullable=True)  # 7 years after deletion for tax compliance
    legal_hold_data = db.Column(db.JSON, nullable=True)  # Original PII backup for legal/compliance purposes

    # Stripe Connect (Payout System)
    stripe_account_id = db.Column(db.String(255), unique=True, nullable=True)
    stripe_onboarding_complete = db.Column(db.Boolean, default=False, nullable=False)
    stripe_payouts_enabled = db.Column(db.Boolean, default=False, nullable=False)
    stripe_charges_enabled = db.Column(db.Boolean, default=False, nullable=False)
    stripe_account_created_at = db.Column(db.DateTime)

    # Gamification System (80/20 engagement)
    total_points = db.Column(db.Integer, default=0, nullable=False)
    current_level = db.Column(db.Integer, default=1, nullable=False)
    seasonal_league = db.Column(db.String(20), default='bronze')

    # Password Reset
    password_reset_token = db.Column(db.String(255), unique=True, nullable=True)
    password_reset_expires = db.Column(db.DateTime, nullable=True)

    # Email Verification
    email_verified = db.Column(db.Boolean, default=False, nullable=False)
    email_verification_token = db.Column(db.String(255), unique=True, nullable=True)
    email_verification_expires = db.Column(db.DateTime, nullable=True)

    # Profile Picture
    profile_picture = db.Column(db.String(255), nullable=True)

    # Referral System
    referral_code = db.Column(db.String(12), nullable=True, index=True)

    # User Preferences (Notifications & Privacy)
    preferences = db.Column(db.JSON, default=lambda: {
        'notifications': {
            'email_job_alerts': True,
            'email_applications': True,
            'email_contracts': True,
            'email_payments': True
        },
        'privacy': {
            'profile_public': True,
            'contact_info_visible': False,
            'rating_visible': True,
            'job_history_visible': True
        }
    }, nullable=False)

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

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

    def generate_reset_token(self):
        """Generate a password reset token"""
        import secrets
        from datetime import timedelta
        
        self.password_reset_token = secrets.token_urlsafe(32)
        self.password_reset_expires = datetime.utcnow() + timedelta(hours=1)
        return self.password_reset_token

    def verify_reset_token(self, token):
        """Verify if the reset token is valid and not expired"""
        if not self.password_reset_token or not self.password_reset_expires:
            return False
        if self.password_reset_token != token:
            return False
        if datetime.utcnow() > self.password_reset_expires:
            return False
        return True

    def clear_reset_token(self):
        """Clear the password reset token"""
        self.password_reset_token = None
        self.password_reset_expires = None

    def generate_verification_token(self):
        """Generate an email verification token"""
        import secrets
        from datetime import timedelta
        
        self.email_verification_token = secrets.token_urlsafe(32)
        self.email_verification_expires = datetime.utcnow() + timedelta(hours=24)
        return self.email_verification_token

    def verify_email_token(self, token):
        """Verify if the email verification token is valid and not expired"""
        if not self.email_verification_token or not self.email_verification_expires:
            return False
        if self.email_verification_token != token:
            return False
        if datetime.utcnow() > self.email_verification_expires:
            return False
        return True
    
    def generate_unsubscribe_token(self):
        """Generate a secure unsubscribe token for marketing emails"""
        import secrets
        import hashlib
        
        # Create a unique token based on user ID and current timestamp
        token_data = f"{self.id}:{self.email}:{datetime.utcnow().isoformat()}:{secrets.token_hex(16)}"
        return hashlib.sha256(token_data.encode()).hexdigest()
    
    @staticmethod
    def verify_unsubscribe_token(token, user_id):
        """Verify unsubscribe token matches the user"""
        from ..extensions import db
        user = db.session.get(User, user_id)
        if not user:
            return None
        # Token is valid if user exists (we don't store token, regenerate on each email)
        return user

    def is_compliance_valid(self):
        """Check if user meets Australian construction industry compliance"""
        issues = []

        # ABN validation
        if not self.abn_number or len(self.abn_number) != 11:
            issues.append("Valid 11-digit ABN required")

        # Insurance requirements
        if not self.public_liability_insurance:
            issues.append("Public liability insurance required")

        if self.public_liability_amount and self.public_liability_amount < 1000000:
            issues.append("Minimum $1M public liability insurance required")

        # Insurance expiry check
        if self.insurance_expiry_date and self.insurance_expiry_date <= date.today():
            issues.append("Insurance has expired")

        # Terms and privacy
        if not self.privacy_consent:
            issues.append("Privacy consent required")

        if not self.terms_accepted:
            issues.append("Terms acceptance required")

        return len(issues) == 0, issues

    def get_gamification_level(self):
        """Calculate user level based on points (500 points per level)"""
        return (self.total_points // 500) + 1

    def points_to_next_level(self):
        """Calculate points needed for next level"""
        current_level_points = (self.current_level - 1) * 500
        next_level_points = self.current_level * 500
        return next_level_points - self.total_points

    def __repr__(self):
        return f'<User {self.email} - {self.role}>'
    
    @property
    def is_account_deleted(self):
        """Check if account is soft deleted"""
        return self.is_deleted
    
    @property
    def can_login(self):
        """Check if user can login (not deleted, active, verified)"""
        return not self.is_deleted and self.is_active and self.email_verified
    
    @classmethod
    def get_active_users(cls):
        """Get only active (non-deleted) users"""
        return cls.query.filter_by(is_deleted=False)
    
    def update_average_rating(self):
        from app.models.rating import Rating
        from app.extensions import db

        ratings = Rating.query.filter_by(rated_id=self.id).all()
        if not ratings:
            self.average_rating = 0.0
        else:
            total = sum(r.overall_score for r in ratings)
            self.average_rating = round(total / len(ratings), 2)

        db.session.commit()

    def is_profile_complete(self):
        """
        Check if worker has completed their profile setup
        Required fields for workers:
        - Basic info (first_name, last_name, email, phone_number, location, abn_number)
        - Primary trade
        - Insurance information
        """
        # Only enforce for workers
        if self.role != 'worker':
            return True
        
        # Check basic required fields
        required_fields = [
            self.first_name,
            self.last_name,
            self.email,
            self.phone_number,
            self.location,
            self.abn_number,
            self.primary_trade  # Workers must specify their trade
        ]
        
        if not all(required_fields):
            return False
        
        # Check insurance requirements
        if not self.public_liability_insurance:
            return False
        
        return True

    def is_payment_setup_complete(self):
        """
        Check if worker has completed their payment setup
        Required for workers to receive payments
        """
        # Only enforce for workers
        if self.role != 'worker':
            return True
        
        # Check if Stripe onboarding is complete and payouts are enabled
        if not self.stripe_account_id:
            return False
        
        if not self.stripe_onboarding_complete:
            return False
        
        if not self.stripe_payouts_enabled:
            return False
        
        return True

    def needs_onboarding(self):
        """
        Check if worker needs to complete onboarding steps
        Returns tuple: (needs_onboarding, missing_steps)
        
        Note: Only profile completion is mandatory. Payment setup is optional but recommended.
        """
        if self.role != 'worker':
            return False, []
        
        missing_steps = []
        
        # Profile completion is mandatory
        if not self.is_profile_complete():
            missing_steps.append('profile')
        
        # Payment setup is optional - workers can skip and set up later
        # We only block if profile is incomplete
        
        return len(missing_steps) > 0, missing_steps

    def to_dict(self):
        """
        Serialize user to dictionary for API responses
        Excludes sensitive fields like password_hash
        """
        return {
            'id': self.id,
            'username': self.username,
            'email': self.email,
            'first_name': self.first_name,
            'last_name': self.last_name,
            'role': self.role,
            'phone_number': self.phone_number,
            'location': self.location,
            'business_name': self.business_name,
            'primary_trade': self.primary_trade,
            'abn_number': self.abn_number,
            'gst_registered': self.gst_registered,
            'jobs_completed': self.jobs_completed,
            'average_rating': float(self.average_rating) if self.average_rating else 0.0,
            'total_reviews': self.total_reviews,
            'response_rate': float(self.response_rate) if self.response_rate else 0.0,
            'public_liability_insurance': self.public_liability_insurance,
            'public_liability_amount': float(self.public_liability_amount) if self.public_liability_amount else None,
            'workers_comp_insurance': self.workers_comp_insurance,
            'insurance_expiry_date': self.insurance_expiry_date.isoformat() if self.insurance_expiry_date else None,
            'worker_control_level': self.worker_control_level,
            'white_card_number': self.white_card_number,
            'white_card_expiry': self.white_card_expiry.isoformat() if self.white_card_expiry else None,
            'is_active': self.is_active,
            'account_status': self.account_status,
            'date_created': self.date_created.isoformat() if self.date_created else None,
            'last_login': self.last_login.isoformat() if self.last_login else None,
            'stripe_onboarding_complete': self.stripe_onboarding_complete,
            'stripe_payouts_enabled': self.stripe_payouts_enabled,
            'stripe_charges_enabled': self.stripe_charges_enabled,
            'total_points': self.total_points,
            'current_level': self.current_level,
            'seasonal_league': self.seasonal_league,
            'profile_picture': self.profile_picture,
            'preferences': self.preferences
        }

    

