"""
PowerShell Data Collection API endpoints.

Compatibility layer for existing PowerShell scripts that update PC data.
Uses API key authentication instead of JWT for automated scripts.
"""

from datetime import datetime
from functools import wraps
from flask import Blueprint, request, current_app

from shopdb.extensions import db
from shopdb.core.models import Machine, Application, InstalledApp
from shopdb.utils.responses import success_response, error_response, ErrorCodes

collector_bp = Blueprint('collector', __name__)


def require_api_key(f):
    """Decorator to require API key authentication."""
    @wraps(f)
    def decorated(*args, **kwargs):
        api_key = request.headers.get('X-API-Key')
        if not api_key:
            api_key = request.args.get('api_key')

        expected_key = current_app.config.get('COLLECTOR_API_KEY')

        if not expected_key:
            return error_response(
                ErrorCodes.INTERNAL_ERROR,
                'Collector API key not configured',
                http_code=500
            )

        if api_key != expected_key:
            return error_response(
                ErrorCodes.UNAUTHORIZED,
                'Invalid API key',
                http_code=401
            )

        return f(*args, **kwargs)
    return decorated


@collector_bp.route('/pc', methods=['POST'])
@require_api_key
def update_pc_info():
    """
    Update PC information from PowerShell collection script.

    Expected JSON payload:
    {
        "hostname": "PC-1234",
        "osname": "Windows 10 Enterprise",
        "osversion": "10.0.19045",
        "lastboottime": "2024-01-15T08:30:00",
        "currentuser": "jsmith",
        "ipaddress": "10.1.2.100",
        "macaddress": "00:11:22:33:44:55",
        "serialnumber": "ABC123",
        "manufacturer": "Dell",
        "model": "OptiPlex 7090"
    }
    """
    data = request.get_json()

    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    hostname = data.get('hostname')
    if not hostname:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'hostname is required')

    # Find the PC by hostname
    pc = Machine.query.filter(
        Machine.hostname.ilike(hostname),
        Machine.pctypeid.isnot(None)
    ).first()

    if not pc:
        # Try to find by machine number if hostname not found
        pc = Machine.query.filter(
            Machine.machinenumber.ilike(hostname),
            Machine.pctypeid.isnot(None)
        ).first()

    if not pc:
        return error_response(
            ErrorCodes.NOT_FOUND,
            f'PC with hostname {hostname} not found',
            http_code=404
        )

    # Update PC fields
    update_fields = {
        'lastzabbixsync': datetime.utcnow(),  # Track last collection time
    }

    if data.get('lastboottime'):
        try:
            update_fields['lastboottime'] = datetime.fromisoformat(
                data['lastboottime'].replace('Z', '+00:00')
            )
        except ValueError:
            pass

    if data.get('currentuser'):
        # Store previous user before updating
        if pc.currentuserid != data['currentuser']:
            update_fields['lastuserid'] = pc.currentuserid
        update_fields['currentuserid'] = data['currentuser']

    if data.get('serialnumber'):
        update_fields['serialnumber'] = data['serialnumber']

    # Update the record
    for key, value in update_fields.items():
        if hasattr(pc, key):
            setattr(pc, key, value)

    db.session.commit()

    return success_response({
        'machineid': pc.machineid,
        'hostname': pc.hostname,
        'updated': True
    }, message='PC info updated')


@collector_bp.route('/apps', methods=['POST'])
@require_api_key
def update_installed_apps():
    """
    Update installed applications for a PC.

    Expected JSON payload:
    {
        "hostname": "PC-1234",
        "apps": [
            {
                "appname": "Microsoft Office",
                "version": "16.0.14326.20454",
                "installdate": "2024-01-10"
            },
            ...
        ]
    }
    """
    data = request.get_json()

    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    hostname = data.get('hostname')
    if not hostname:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'hostname is required')

    apps = data.get('apps', [])
    if not apps:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'apps list is required')

    # Find the PC
    pc = Machine.query.filter(
        Machine.hostname.ilike(hostname),
        Machine.pctypeid.isnot(None)
    ).first()

    if not pc:
        return error_response(
            ErrorCodes.NOT_FOUND,
            f'PC with hostname {hostname} not found',
            http_code=404
        )

    updated_count = 0
    created_count = 0
    skipped_count = 0

    for app_data in apps:
        app_name = app_data.get('appname')
        if not app_name:
            skipped_count += 1
            continue

        # Find the application in the database
        app = Application.query.filter(
            Application.appname.ilike(app_name)
        ).first()

        if not app:
            # Skip apps not in our tracked list
            skipped_count += 1
            continue

        # Check if already installed
        installed = InstalledApp.query.filter_by(
            machineid=pc.machineid,
            appid=app.appid
        ).first()

        if installed:
            # Update version if changed
            new_version = app_data.get('version')
            if new_version and installed.installedversion != new_version:
                installed.installedversion = new_version
                installed.modifieddate = datetime.utcnow()
                updated_count += 1
        else:
            # Create new installed app record
            installed = InstalledApp(
                machineid=pc.machineid,
                appid=app.appid,
                installedversion=app_data.get('version'),
                installdate=datetime.utcnow()
            )
            db.session.add(installed)
            created_count += 1

    db.session.commit()

    return success_response({
        'hostname': hostname,
        'machineid': pc.machineid,
        'created': created_count,
        'updated': updated_count,
        'skipped': skipped_count
    }, message='Installed apps updated')


@collector_bp.route('/heartbeat', methods=['POST'])
@require_api_key
def pc_heartbeat():
    """
    Record PC online status / heartbeat.

    Expected JSON payload:
    {
        "hostname": "PC-1234"
    }

    Or batch update:
    {
        "hostnames": ["PC-1234", "PC-1235", "PC-1236"]
    }
    """
    data = request.get_json()

    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    hostnames = data.get('hostnames', [])
    if not hostnames and data.get('hostname'):
        hostnames = [data['hostname']]

    if not hostnames:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'hostname or hostnames required')

    updated = 0
    not_found = []

    for hostname in hostnames:
        pc = Machine.query.filter(
            Machine.hostname.ilike(hostname),
            Machine.pctypeid.isnot(None)
        ).first()

        if pc:
            pc.lastzabbixsync = datetime.utcnow()
            updated += 1
        else:
            not_found.append(hostname)

    db.session.commit()

    return success_response({
        'updated': updated,
        'notfound': not_found,
        'timestamp': datetime.utcnow().isoformat()
    }, message=f'{updated} PC(s) heartbeat recorded')


@collector_bp.route('/bulk', methods=['POST'])
@require_api_key
def bulk_update():
    """
    Bulk update multiple PCs at once.

    Expected JSON payload:
    {
        "pcs": [
            {
                "hostname": "PC-1234",
                "currentuser": "jsmith",
                "lastboottime": "2024-01-15T08:30:00"
            },
            ...
        ]
    }
    """
    data = request.get_json()

    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    pcs = data.get('pcs', [])
    if not pcs:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'pcs list is required')

    updated = 0
    not_found = []
    errors = []

    for pc_data in pcs:
        hostname = pc_data.get('hostname')
        if not hostname:
            continue

        pc = Machine.query.filter(
            Machine.hostname.ilike(hostname),
            Machine.pctypeid.isnot(None)
        ).first()

        if not pc:
            not_found.append(hostname)
            continue

        try:
            pc.lastzabbixsync = datetime.utcnow()

            if pc_data.get('currentuser'):
                if pc.currentuserid != pc_data['currentuser']:
                    pc.lastuserid = pc.currentuserid
                pc.currentuserid = pc_data['currentuser']

            if pc_data.get('lastboottime'):
                try:
                    pc.lastboottime = datetime.fromisoformat(
                        pc_data['lastboottime'].replace('Z', '+00:00')
                    )
                except ValueError:
                    pass

            updated += 1

        except Exception as e:
            errors.append({'hostname': hostname, 'error': str(e)})

    db.session.commit()

    return success_response({
        'updated': updated,
        'notfound': not_found,
        'errors': errors,
        'timestamp': datetime.utcnow().isoformat()
    }, message=f'{updated} PC(s) updated')


@collector_bp.route('/status', methods=['GET'])
@require_api_key
def collector_status():
    """Check collector API status and configuration."""
    return success_response({
        'status': 'ok',
        'timestamp': datetime.utcnow().isoformat(),
        'endpoints': [
            'POST /api/collector/pc',
            'POST /api/collector/apps',
            'POST /api/collector/heartbeat',
            'POST /api/collector/bulk',
            'GET /api/collector/status'
        ]
    })
