#!/usr/bin/env python3
"""
Home Assistant REST API Client

This script provides utilities to interact with Home Assistant via the REST API.
Supports getting states, calling services, and querying configuration.

Usage:
    # Get all entity states
    python3 api_client.py --url http://homeassistant.local:8123 --token YOUR_TOKEN get-states

    # Get specific entity state
    python3 api_client.py --url http://homeassistant.local:8123 --token YOUR_TOKEN get-state light.living_room

    # Call a service
    python3 api_client.py --url http://homeassistant.local:8123 --token YOUR_TOKEN call-service light turn_on --entity light.living_room --data '{"brightness": 255}'

    # List all services
    python3 api_client.py --url http://homeassistant.local:8123 --token YOUR_TOKEN list-services

Environment variables:
    HA_URL: Home Assistant URL (e.g., http://homeassistant.local:8123)
    HA_TOKEN: Long-lived access token
"""

import argparse
import json
import os
import sys
from typing import Any, Dict, Optional
import urllib.request
import urllib.error
import urllib.parse


class HomeAssistantClient:
    """Client for interacting with Home Assistant REST API."""

    def __init__(self, url: str, token: str):
        """Initialize the client with URL and access token."""
        self.url = url.rstrip('/')
        self.token = token
        self.headers = {
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json'
        }

    def _request(self, endpoint: str, method: str = 'GET', data: Optional[Dict] = None) -> Any:
        """Make a request to the Home Assistant API."""
        url = f"{self.url}/api/{endpoint}"

        req = urllib.request.Request(url, headers=self.headers, method=method)

        if data is not None:
            req.data = json.dumps(data).encode('utf-8')

        try:
            with urllib.request.urlopen(req) as response:
                return json.loads(response.read().decode('utf-8'))
        except urllib.error.HTTPError as e:
            error_body = e.read().decode('utf-8')
            print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
            print(f"Response: {error_body}", file=sys.stderr)
            sys.exit(1)
        except urllib.error.URLError as e:
            print(f"URL Error: {e.reason}", file=sys.stderr)
            sys.exit(1)

    def get_states(self) -> list:
        """Get all entity states."""
        return self._request('states')

    def get_state(self, entity_id: str) -> Dict:
        """Get state of a specific entity."""
        return self._request(f'states/{entity_id}')

    def get_services(self) -> Dict:
        """Get all available services."""
        return self._request('services')

    def get_config(self) -> Dict:
        """Get Home Assistant configuration."""
        return self._request('config')

    def call_service(self, domain: str, service: str, entity_id: Optional[str] = None,
                    service_data: Optional[Dict] = None) -> list:
        """Call a service."""
        data = {}
        if entity_id:
            data['entity_id'] = entity_id
        if service_data:
            data.update(service_data)

        return self._request(f'services/{domain}/{service}', method='POST', data=data if data else None)

    def get_error_log(self) -> str:
        """Get the error log."""
        url = f"{self.url}/api/error_log"
        req = urllib.request.Request(url, headers=self.headers)
        with urllib.request.urlopen(req) as response:
            return response.read().decode('utf-8')


def main():
    parser = argparse.ArgumentParser(description='Home Assistant REST API Client')
    parser.add_argument('--url', help='Home Assistant URL',
                       default=os.getenv('HA_URL'))
    parser.add_argument('--token', help='Long-lived access token',
                       default=os.getenv('HA_TOKEN'))

    subparsers = parser.add_subparsers(dest='command', help='Command to execute')

    # get-states command
    subparsers.add_parser('get-states', help='Get all entity states')

    # get-state command
    state_parser = subparsers.add_parser('get-state', help='Get specific entity state')
    state_parser.add_argument('entity_id', help='Entity ID')

    # list-services command
    subparsers.add_parser('list-services', help='List all available services')

    # get-config command
    subparsers.add_parser('get-config', help='Get Home Assistant configuration')

    # call-service command
    service_parser = subparsers.add_parser('call-service', help='Call a service')
    service_parser.add_argument('domain', help='Service domain (e.g., light, switch)')
    service_parser.add_argument('service', help='Service name (e.g., turn_on, turn_off)')
    service_parser.add_argument('--entity', help='Entity ID to target')
    service_parser.add_argument('--data', help='Service data as JSON string')

    # get-error-log command
    subparsers.add_parser('get-error-log', help='Get the error log')

    args = parser.parse_args()

    if not args.url:
        print("Error: Home Assistant URL is required (use --url or set HA_URL environment variable)", file=sys.stderr)
        sys.exit(1)

    if not args.token:
        print("Error: Access token is required (use --token or set HA_TOKEN environment variable)", file=sys.stderr)
        sys.exit(1)

    if not args.command:
        parser.print_help()
        sys.exit(1)

    client = HomeAssistantClient(args.url, args.token)

    if args.command == 'get-states':
        result = client.get_states()
    elif args.command == 'get-state':
        result = client.get_state(args.entity_id)
    elif args.command == 'list-services':
        result = client.get_services()
    elif args.command == 'get-config':
        result = client.get_config()
    elif args.command == 'call-service':
        service_data = json.loads(args.data) if args.data else None
        result = client.call_service(args.domain, args.service, args.entity, service_data)
    elif args.command == 'get-error-log':
        result = client.get_error_log()
        print(result)
        return

    print(json.dumps(result, indent=2))


if __name__ == '__main__':
    main()
