# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json, logging, operator, requests
from requests.auth import HTTPBasicAuth
from typing import Optional
from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES
from typing import List, Dict, Optional, Tuple, Union

LOGGER = logging.getLogger(__name__)

RESOURCE_ENDPOINTS = {
    #get configurations
    "switches": "/stats/switches",
    "description": "/stats/desc",
    "flows": "/stats/flow",
    "port_description":"/stats/portdesc",
    "switch_info":"/v1.0/topology/switches",
    "links_info":"/v1.0/topology/links",
    #add flow
    "flow_add": "/stats/flowentry/add",
    #Delete all matching flow entries of the switch.
    "flow_delete": "/stats/flowentry/delete",
    "flowentry_delete":"/stats/flowentry/clear", #according to dpid

}

HTTP_OK_CODES = {
    200,    # OK
    201,    # Created
    202,    # Accepted
    204,    # No Content
}

# Utility function to find and extract a specific key from a resource.
def find_key(resource: Tuple[str, str], key: str) -> Union[dict, str, None]:
    try:
        return json.loads(resource[1])[key]
    except KeyError:
        LOGGER.warning(f"Key '{key}' not found in resource.")
        return None

def get_switches(root_url: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> List[Dict]:
    url = f"{root_url}{RESOURCE_ENDPOINTS['switches']}"
    result = []
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        switches = response.json()
        LOGGER.info(f"Successfully retrieved switches: {switches}")
        result = switches
    except requests.exceptions.Timeout:
        LOGGER.exception(f"Timeout connecting to {url}")
    except requests.exceptions.RequestException as e:
        LOGGER.exception(f"Error retrieving switches: {str(e)}")
    return result

def get_switches_information(root_url: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> List[Dict]:
    url = f"{root_url}{RESOURCE_ENDPOINTS['switch_info']}"
    result = []
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        switches_info = response.json()
        LOGGER.info(f"Successfully retrieved switches: {switches_info}")
        result = switches_info
    except requests.exceptions.Timeout:
        LOGGER.exception(f"Timeout connecting to {url}")
    except requests.exceptions.RequestException as e:
        LOGGER.exception(f"Error retrieving switches: {str(e)}")
    return result

def get_links_information(root_url: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> List[Dict]:
    url = f"{root_url}{RESOURCE_ENDPOINTS['links_info']}"
    result = []
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        links_info = response.json()
        LOGGER.info(f"Successfully retrieved switches: {links_info}")
        result = links_info
    except requests.exceptions.Timeout:
        LOGGER.exception(f"Timeout connecting to {url}")
    except requests.exceptions.RequestException as e:
        LOGGER.exception(f"Error retrieving switches: {str(e)}")
    return result

def get_flows(root_url: str, dpid: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> List[Dict]:
    url = f"{root_url}{RESOURCE_ENDPOINTS['flows']}/{dpid}"
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        flows = response.json()
        LOGGER.info(f"Successfully retrieved flow rules for DPID {dpid}")
        return flows
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to retrieve flow rules for DPID {dpid}: {str(e)}")
        return []
    
#get description 
def get_desc(root_url: str, dpid: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> Dict:
    url = f"{root_url}{RESOURCE_ENDPOINTS['description']}/{dpid}"
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        desc = response.json()
        LOGGER.info(f"Successfully retrieved description for DPID {dpid}: {desc}")
        return desc
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to retrieve description for DPID {dpid}: {str(e)}")
        return {}
    
def get_port_desc(root_url: str, dpid: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> Dict:
    url = f"{root_url}{RESOURCE_ENDPOINTS['port_description']}/{dpid}"
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        port_desc = response.json()
        LOGGER.info(f"Successfully retrieved description for DPID {dpid}: {port_desc}")
        return port_desc
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to retrieve description for DPID {dpid}: {str(e)}")
        return {}

##according to dpid
def del_flow_entry(root_url: str, dpid: str, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> Dict:
    url = f"{root_url}{RESOURCE_ENDPOINTS['flowentry_delete']}/{dpid}"
    try:
        response = requests.delete(url, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        flow_desc = response.json()
        LOGGER.info(f"Successfully retrieved description for DPID {dpid}: {flow_desc}")
        return flow_desc
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to retrieve description for DPID {dpid}: {str(e)}")
        return {}

#  to delete a flow based on match criteria.
def delete_flow(root_url: str, flow_data: dict, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> bool:
    url = f"{root_url}{RESOURCE_ENDPOINTS['flow_delete']}"
    try:
        response = requests.post(url, json=flow_data, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        LOGGER.info(f"Flow configuration deleted successfully for DPID {flow_data.get('dpid')}.")
        return True
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to delete flow configuration for DPID {flow_data.get('dpid')}: {str(e)}")
        return False
    
def add_flow(root_url: str, flow_data: dict, auth: Optional[HTTPBasicAuth] = None, timeout: Optional[int] = None) -> bool:
    url = f"{root_url}{RESOURCE_ENDPOINTS['flow_add']}"
    LOGGER.info(f"Posting flow data: {flow_data} (type: {type(flow_data)}) to URL: {url}") 
    try:
        response = requests.post(url, json=flow_data, timeout=timeout, verify=False, auth=auth)
        response.raise_for_status()
        LOGGER.info("Flow configuration added successfully.")
        return True
    except requests.exceptions.RequestException as e:
        LOGGER.error(f"Failed to add flow configuration: {str(e)}")
        return False



