use std::{io::{Read, Write}, net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}, os::unix::{self, net::UnixStream}};

use crate::{PsqlResult, PsqlError};
use crate::message::{BackendMessage, Message, MessageError};
use crate::dns::resolve_url;

pub struct PsqlSession {
    location: Location,
    connection: SocketConnection,
}

impl PsqlSession {
    //pub fn read_messages(&mut self) -> Result<Vec<BackendMessage>, PsqlError> {
    //    Ok(())
    //}
    
    pub fn connect(location: Option<Location>) -> PsqlResult<PsqlSession> {
        //Use provided location or default to 127.0.0.1:5432
        let location = location.unwrap_or(Location::Ip(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5432)));
    
        let connection = location.connect().unwrap();
    
        Ok(PsqlSession{
            location,
            connection,
        })
    }
    
    pub fn wait_for_response(&mut self) -> PsqlResult<Message> {
        todo!()
    }

}

#[derive(Debug)]
pub enum Location {
    UnixSocket(String),
    URI(String),
    Ip(SocketAddr),
}

type ConnectionError = (Location, String);

impl Location {
    fn connect(&self) -> Result<SocketConnection, ConnectionError> {
        Ok(match self {
            Location::UnixSocket(path) => SocketConnection::Unix(UnixStream::connect(path).unwrap()),
            Location::URI(uri) => SocketConnection::Tcp(TcpStream::connect(SocketAddr::new(resolve_url(uri.clone()).unwrap(), 5432)).unwrap()),
            Location::Ip(addr) => SocketConnection::Tcp(TcpStream::connect(addr).unwrap()),
        })
    }
}

pub enum SocketConnection {
    Unix(UnixStream),
    Tcp(TcpStream),
}

impl Read for SocketConnection {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        match self {
            SocketConnection::Unix(unix_stream) => unix_stream.read(buf),
            SocketConnection::Tcp(tcp_stream) => tcp_stream.read(buf),
        }
    }
}

impl Write for SocketConnection {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self {
            SocketConnection::Unix(unix_stream) => unix_stream.write(buf),
            SocketConnection::Tcp(tcp_stream) => tcp_stream.write(buf),
        }
    }

    fn flush(&mut self) -> std::io::Result<()> {
        match self {
            SocketConnection::Unix(unix_stream) => unix_stream.flush(),
            SocketConnection::Tcp(tcp_stream) => tcp_stream.flush(),
        }
    }
}

//pub fn startup(session: &mut PsqlSession) -> PsqlResult<()> {
//    let startup_message = Message {
//        message_type: MessageType::Startup,
//    };
//
//    send_message(startup_message)?;
//
//    let response = wait_for_response(session)?;
//
//    match response.message_type {
//        MessageType::ErrorResponse => todo!(),
//        MessageType::NoticeResponse => todo!(),
//        MessageType::AuthenticationOk => todo!(), //No auth needed. 
//        MessageType::AuthenticationRequest(authentication_request_type) => todo!(), //Auth Needed
//        _ => Err(PsqlError::UnexpectedMessageType(response.message_type))?
//    }
//
//    
//    
//    Ok(())
//}