import requests
import argparse
import json
import sys
import time
import socket
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

# Forced IPv4 Adapter
class IPv4Adapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        kwargs['socket_options'] = HTTPAdapter.poolmanager_conf['socket_options'] + [
            (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1),
            (socket.IPPROTO_IP, socket.IP_TTL, 20)
        ]
        if socket.has_ipv6:
            # This is a bit of a hack to prefer IPv4 in some environments
            pass
        return super(IPv4Adapter, self).init_poolmanager(*args, **kwargs)

class DishHomeTool:
    def __init__(self, username=None, password=None):
        self.base_url = "https://care.dishhome.com.np"
        self.session = requests.Session()
        # Prefer IPv4 by mounting a specific adapter if needed, 
        # but for now let's just focus on timeouts and logging.
        self.timeout = 100 # 25 seconds timeout
        self.username = username
        self.password = password
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
            "Accept-Language": "en-US,en;q=0.9",
        }
        self.is_logged_in = False

    def check_login_status(self):
        print(f"[{time.strftime('%H:%M:%S')}] Checking login status...")
        try:
            response = self.session.get(f"{self.base_url}/Home/Dashboard", headers=self.headers, allow_redirects=False, timeout=self.timeout)
            if response.status_code == 200:
                print(f"[{time.strftime('%H:%M:%S')}] Already logged in.")
                self.is_logged_in = True
                return True
        except Exception as e:
            print(f"[{time.strftime('%H:%M:%S')}] Login check failed: {e}")
        self.is_logged_in = False
        return False

    def login(self, force=False, retries=3):
        if self.is_logged_in and not force:
            return True, "Already logged in."

        if not self.username or not self.password:
            return False, "Username and password are required for automated login."

        attempt = 0
        while attempt < retries:
            attempt += 1
            print(f"[{time.strftime('%H:%M:%S')}] Attempting login for {self.username} (Attempt {attempt}/{retries})...")
            try:
                # 1. Get the login page to retrieve the __RequestVerificationToken
                login_url = f"{self.base_url}/Account/Login"
                response = self.session.get(login_url, headers=self.headers, timeout=self.timeout)
                soup = BeautifulSoup(response.text, 'html.parser')
                token_input = soup.find('input', {'name': '__RequestVerificationToken'})
                
                if not token_input:
                    if attempt < retries:
                        print(f"[{time.strftime('%H:%M:%S')}] Token not found, retrying...")
                        time.sleep(2)
                        continue
                    return False, "Could not find RequestVerificationToken on login page."
                
                token = token_input['value']

                # 2. Perform the POST login
                payload = {
                    "__RequestVerificationToken": token,
                    "Email": self.username,
                    "Password": self.password,
                    "login": "Login"
                }
                
                # The login redirects on success
                print(f"[{time.strftime('%H:%M:%S')}] Sending login POST...")
                response = self.session.post(login_url, data=payload, headers=self.headers, allow_redirects=True, timeout=self.timeout)
                
                if "Logout" in response.text or "/Account/LogOff" in response.text:
                    print(f"[{time.strftime('%H:%M:%S')}] Login successful.")
                    self.is_logged_in = True
                    return True, "Login successful."
                else:
                    print(f"[{time.strftime('%H:%M:%S')}] Login failed. Page content length: {len(response.text)}")
                    if attempt < retries:
                        time.sleep(2)
                        continue
                    self.is_logged_in = False
                    return False, "Login failed. Check credentials or site status."

            except Exception as e:
                print(f"[{time.strftime('%H:%M:%S')}] Login attempt {attempt} failed: {e}")
                if attempt < retries:
                    time.sleep(2)
                    continue
                return False, f"Login error: {str(e)}"
        
        return False, "Max login retries exceeded."

    def get_verification_token(self, url=None, retries=3):
        if not url:
            url = f"{self.base_url}/Subscriber/GetCustomerDetailBySTBIDorCASIDorCHIPID"
        
        attempt = 0
        while attempt < retries:
            attempt += 1
            print(f"[{time.strftime('%H:%M:%S')}] Fetching verification token from {url} (Attempt {attempt}/{retries})...")
            try:
                response = self.session.get(url, headers=self.headers, timeout=self.timeout)
                soup = BeautifulSoup(response.text, 'html.parser')
                token_input = soup.find('input', {'name': '__RequestVerificationToken'})
                token = token_input['value'] if token_input else None
                print(f"[{time.strftime('%H:%M:%S')}] Token found: {'Yes' if token else 'No'}")
                if token:
                    return token
                if attempt < retries:
                    time.sleep(2)
                    continue
            except Exception as e:
                print(f"[{time.strftime('%H:%M:%S')}] Token fetch attempt {attempt} error: {e}")
                if attempt < retries:
                    time.sleep(2)
                    continue
        return None

    def get_customer_detail(self, customer_id, detail_by="Customer Detail By CASID/CustomerId", token=None):
        # reuse provided token or fetch a new one
        main_url = f"{self.base_url}/Subscriber/GetCustomerDetailBySTBIDorCASIDorCHIPID"
        try:
            if not token:
                token = self.get_verification_token(main_url)
            
            # Use the same URL for POST as shown in the form action
            url = main_url
            
            payload = {
                "__RequestVerificationToken": token,
                "CustomerDetailBy": detail_by,
                "CustomerID/InternetUserID/MobileNo.": customer_id,
                "GetCustomerDetailBySTBIDorCASIDorCHIPID": customer_id,  # Try alternative name
                "STBIDorCASIDorCHIPID": customer_id # Another try
            }
            
            if not token:
                # If we couldn't find the token, try without it but log it
                del payload["__RequestVerificationToken"]
        
            ajax_headers = self.headers.copy()
            ajax_headers.update({
                "Accept": "application/json, text/javascript, */*; q=0.01",
                "X-Requested-With": "XMLHttpRequest",
                "Referer": main_url,
                "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
            })
            
            print(f"[{time.strftime('%H:%M:%S')}] Fetching customer detail for {customer_id}...")
            response = self.session.post(url, data=payload, headers=ajax_headers, timeout=self.timeout)
            
            if response.status_code == 302:
                # This means our session might have expired or login failed
                return {"error": "Session expired or authentication failed. Please check login."}
            
            if "application/json" in response.headers.get('Content-Type', ''):
                return response.json()
            else:
                # If it's HTML, it might be the page itself or an error page
                return {"error": "Received non-JSON response.", "details": response.text[:200]}
                
        except Exception as e:
            return {"error": str(e)}

def main():
    parser = argparse.ArgumentParser(description="DishHome Customer Detail Automation Tool")
    parser.add_argument("--id", help="Single Customer ID / CASID / Mobile No.")
    parser.add_argument("--file", help="JSON file containing a list of Customer IDs")
    parser.add_argument("--username", default="LBTWL-D57", help="DishHome Portal Username")
    parser.add_argument("--password", default="Dishhome@321", help="DishHome Portal Password")
    parser.add_argument("--output", help="Save results to a JSON file")

    args = parser.parse_args()

    if not args.id and not args.file:
        parser.error("Either --id or --file must be provided.")

    tool = DishHomeTool(username=args.username, password=args.password)
    
    # Perform Login
    success, msg = tool.login()
    if not success:
        print(json.dumps({"error": msg}, indent=4))
        sys.exit(1)
        
    ids = []
    if args.id:
        ids.append(args.id)
    
    if args.file:
        try:
            with open(args.file, 'r') as f:
                file_ids = json.load(f)
                if isinstance(file_ids, list):
                    ids.extend([str(i) for i in file_ids])
                else:
                    print(f"Error: {args.file} must contain a list of IDs.")
                    sys.exit(1)
        except Exception as e:
            print(f"Error reading {args.file}: {e}")
            sys.exit(1)

    results = []
    for customer_id in ids:
        result = tool.get_customer_detail(customer_id)
        results.append({
            "id": customer_id,
            "data": result
        })
    
    # If single request, just output the data object as before for compatibility
    output_data = results[0]["data"] if len(results) == 1 and not args.file else results

    if args.output:
        with open(args.output, 'w') as f:
            json.dump(output_data, f, indent=4)
        print(f"Results saved to {args.output}")
    else:
        print(json.dumps(output_data, indent=4))

if __name__ == "__main__":
    main()

