import os
import json
import csv
import traceback
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, unquote, parse_qs
import IP2Location
from phonenumbers.phonenumberutil import NumberParseException
from phonenumbers import parse, geocoder

# Инициализация базы данных IP
database = IP2Location.IP2Location(os.path.join("IP2LOCATION-LITE-DB3.BIN"))

# Глобальная переменная для кэширования базы телефонных кодов
russian_phone_db = None

def load_russian_phone_database():
    """Загружает базу данных российских телефонных кодов из CSV файла"""
    global russian_phone_db
    
    if russian_phone_db is None:
        russian_phone_db = []
        try:
            # Предполагаем, что файл находится в той же директории
            csv_path = os.path.join(os.path.dirname(__file__), "DEF-9xx.csv")
            if os.path.exists(csv_path):
                with open(csv_path, "r", encoding='utf-8') as f:
                    reader = csv.reader(f, delimiter="\t")
                    for i, line in enumerate(reader):
                        if i != 0 and line:  # Пропускаем заголовок
                            parts = line[0].split(";")
                            if len(parts) >= 6:
                                zone = parts[0]
                                start_range = int(parts[1])
                                end_range = int(parts[2])
                                region = parts[5].strip()
                                russian_phone_db.append({
                                    'zone': zone,
                                    'start': start_range,
                                    'end': end_range,
                                    'region': region
                                })
                print(f"Loaded {len(russian_phone_db)} phone number ranges from database")
            else:
                print(f"Warning: Phone database file not found at {csv_path}")
        except Exception as e:
            print(f"Error loading phone database: {e}")
    
    return russian_phone_db

def get_region_by_ip(ip_address):
    """Получает регион по IP-адресу"""
    try:
        rec = database.get_all(ip_address)
        return {
            "region_name": rec.region,
        }
    except Exception as e:
        return {"region_name": "не определен"}
        #raise ValueError(f"Error processing IP address: {str(e)}")

def normalize_phone(phone):
    """Нормализует номер телефона, удаляя лишние символы"""
    # Удаляем все нецифровые символы кроме плюса в начале
    normalized = phone.replace("-", "").replace("(", "").replace(")", "").replace(" ", "")
    return normalized

def get_region_by_phone_russia(zone, number):
    """Ищет регион для российского номера в базе данных"""
    phone_db = load_russian_phone_database()
    if not phone_db:
        return None
    
    for record in phone_db:
        if record['zone'] == zone and record['start'] <= number <= record['end']:
            return record['region']
    
    return None

def get_region_by_phone(phone):
    """Определяет регион по номеру телефона"""
    # Нормализуем номер
    normalized_phone = normalize_phone(phone)
    
    # Для российских номеров
    if normalized_phone.startswith("+7") and len(normalized_phone) >= 5:
        if normalized_phone[2] == "9":  # Мобильный номер
            zone = normalized_phone[2:5]
            try:
                number = int(normalized_phone[5:])
                region = get_region_by_phone_russia(zone, number)
                if region:
                    return {
                        "region_name": region,
                    }
            except ValueError:
                pass  # Переходим к общему методу
    
    elif normalized_phone.startswith("8") and len(normalized_phone) >= 4:
        if normalized_phone[1] == "9":  # Мобильный номер
            zone = normalized_phone[1:4]
            try:
                number = int(normalized_phone[4:])
                region = get_region_by_phone_russia(zone, number)
                if region:
                    return {
                        "region_name": region,
                    }
            except ValueError:
                pass  # Переходим к общему методу
    
    # Общий метод для всех остальных номеров
    try:
        parsed_number = parse(normalized_phone)
        region = geocoder.description_for_number(parsed_number, 'ru')
        if region:
            return {
                "region_name": region,
            }
    except NumberParseException:
        pass
    return {"region_name": "не определен"}
    #raise ValueError("Could not determine region from phone number")

def detect_input_type(input_string):
    """Определяет тип введенных данных (IP или телефон)"""
    # Проверяем, похоже ли на IP-адрес
    parts = input_string.replace(' ', '').split('.')
    if len(parts) == 4:
        try:
            for part in parts:
                if not 0 <= int(part) <= 255:
                    return "phone"
            return "ip"
        except ValueError:
            return "phone"
    
    # Проверяем, похоже ли на IPv6 (упрощенно)
    if ':' in input_string and '.' not in input_string:
        return "ip"
    
    # Проверяем, содержит ли телефонные символы
    digits = sum(c.isdigit() for c in input_string)
    if digits >= 7:  # Минимальная длина номера телефона
        return "phone"
    
    return "unknown"

class LocationHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            # Парсим URL
            parsed_path = urlparse(self.path)
            path = unquote(parsed_path.path)
            
            # Получаем параметры из query string
            query_params = parse_qs(parsed_path.query)
            
            # Извлекаем данные из пути или query параметров
            input_data = path.lstrip('/')
            
            # Если в пути нет данных, проверяем query параметры
            if not input_data:
                if 'ip' in query_params:
                    input_data = query_params['ip'][0]
                    input_type = "ip"
                elif 'phone' in query_params:
                    input_data = query_params['phone'][0]
                    input_type = "phone"
                else:
                    self.send_error(400, "IP address or phone number is required")
                    return
            else:
                # Автоопределение типа
                input_type = detect_input_type(input_data)
            
            # Получаем данные в зависимости от типа
            if input_type == "ip":
                response_data = get_region_by_ip(input_data)
            elif input_type == "phone":
                response_data = get_region_by_phone(input_data)
            else:
                self.send_error(400, "Could not determine input type (should be IP address or phone number)")
                return
            
            # Отправляем успешный ответ
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            self.wfile.write(json.dumps(response_data, ensure_ascii=False).encode('utf-8'))
            
        except ValueError as e:
            self.send_error(400, f"Bad request: {str(e)}")
        except Exception as e:
            self.send_error(500, f"Internal server error: {str(e)}")
            print(f"Error processing GET request: {traceback.format_exc()}")
    
    def do_POST(self):
        try:
            content_length = int(self.headers.get('Content-Length', 0))
            post_data = self.rfile.read(content_length).decode('utf-8')
            
            if not post_data:
                self.send_error(400, "Request body is empty")
                return
            
            # Пытаемся распарсить JSON
            data = json.loads(post_data)
            
            # Проверяем, что передан IP или телефон
            if 'ip' in data:
                input_data = data['ip']
                input_type = "ip"
            elif 'phone' in data:
                input_data = data['phone']
                input_type = "phone"
            else:
                # Автоопределение
                input_data = data.get('query', '')
                if not input_data:
                    self.send_error(400, "IP address or phone number is required")
                    return
                input_type = detect_input_type(input_data)
            
            # Получаем данные
            if input_type == "ip":
                response_data = get_region_by_ip(input_data)
            elif input_type == "phone":
                response_data = get_region_by_phone(input_data)
            else:
                self.send_error(400, "Could not determine input type (should be IP address or phone number)")
                return
            
            # Отправляем ответ
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            self.wfile.write(json.dumps(response_data, ensure_ascii=False).encode('utf-8'))
            
        except json.JSONDecodeError:
            self.send_error(400, "Invalid JSON format")
        except ValueError as e:
            self.send_error(400, f"Bad request: {str(e)}")
        except Exception as e:
            self.send_error(500, f"Internal server error: {str(e)}")
            print(f"Error processing POST request: {traceback.format_exc()}")
    
    def log_message(self, format, *args):
        # Отключаем стандартное логирование запросов
        pass

def run_server(port=8887):
    # Предзагружаем базу телефонных номеров
    load_russian_phone_database()
    
    server_address = ('0.0.0.0', port)
    httpd = HTTPServer(server_address, LocationHandler)
    print(f"Starting location service on port {port}...")
    print(f"IP database loaded")
    print(f"Phone database: {len(russian_phone_db) if russian_phone_db else 0} ranges loaded")
    print("Supported endpoints:")
    print("  GET /<ip_address> - get location by IP")
    print("  GET /<phone_number> - get location by phone")
    print("  GET /?ip=<ip_address> - get location by IP (query param)")
    print("  GET /?phone=<phone_number> - get location by phone (query param)")
    print("  POST / with JSON body: {\"ip\": \"...\"} or {\"phone\": \"...\"}")
    print("Use Ctrl+C to stop the server")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\nServer stopped.")
    finally:
        httpd.server_close()

if __name__ == '__main__':
    run_server()
